/*****************************************************************************
Copyright 2006 Kervin L. Pierre
    EMAIL: otlkcon@adevsoft.com

This file is part of Otlkcon.

Otlkcon is free software; you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation; either version 2.1 of the License, or
(at your option) any later version.

Otlkcon is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
Lesser GNU General Public License for more details.

You should have received a copy of the Lesser GNU General Public License
along with Otlkcon; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA

*******************************************************************************/

#include "StdAfx.h"
#include "O_IProp.h"
#include "O_ITable.h"
#include "otlkcon_generic_msgstore.h"

#include "otlkcon_debug.h"

extern map< O_ENTRYID *, void *, otlkcon_entryid_less >      otlkcon_event_objects;
extern CRITICAL_SECTION    otlkcon_objects_cs;

O_IProp::O_IProp(void)
{
    lastCalledFunctionID = 0;
    databaseFilename = NULL;
    isTransacted = false;
    tblVersion = 0;
    //we using object pointer as a transaction ID
	//that is not UNIQUE between different processes
	objNum = (long)this;

    parent          = NULL;
	tableName       = NULL;
    diskTableName    = NULL;
    entryID         = NULL;
    className       = NULL;
    objName = _strdup(" ");
    
    memObj = NULL;
//    sqlObj = NULL;
    objectId = 0;

    // FIXME: We need to find the best scope for this.
    srand((unsigned int)GetTickCount());

	InitializeCriticalSection(&objectCS);
}

O_IProp::~O_IProp(void)
{
    OTLKCON_LOG_PRINTF(OTLKCON_LOG_LEVEL_INFO,  OTLKCON_LOG_TARGET_ALL, L"%S->O_IProp::~O_IProp(): Goodbye cruel world!....", className);
	DeleteCriticalSection(&objectCS);
    Destroy();
}

HRESULT O_IProp::CreateEntryID( O_ENTRYID *eid, const MAPIUID* sg, MAPIUID ig, MAPIUID tg )
{
	HRESULT hResult = E_INVALIDARG;
	if(NULL != eid)
	{
		eid->supportedInterfaces =  OTLKCON_IMAPIPROP_CLASS | OTLKCON_IUNKNOWN_CLASS;
		// If ig is GUID_NULL, then get a new guid
		if( memcmp(&ig,&otlkcon_null_guid,sizeof(MAPIUID)) == 0 )
		{
			hResult = NewMAPIUID( &ig );
		}
		eid->storeInstanceGuid	= *sg;
		eid->instanceGuid		= ig;
		eid->typeGuid			= tg;
		hResult = S_OK;
	} // if
	return hResult;
}


HRESULT O_IProp::Create( otlkcon_generic_msgstore *mstr, MAPIUID ig, MAPIUID tg )
{
    HRESULT result = S_OK;
    int sqlRes;
    int rowCount;

    //Henry 11.12.2005
	//TODO
	//I think better to use 0 there and use QueryInterface or AddRef
	//while returning reference to client
	//So we need less code to check supported interfaces
	//Object will decide itself that interface to support
	cRef = 1;

    msgstore = mstr;
    if( msgstore == NULL )
    {
        OTLKCON_DEBUGBREAK;
    }

    //dont need this any more
	//objNum = msgstore->GetObjCount();

    memObj = new otlkcon_memory( msgstore->memObj );
    
    // Store the database file name for later use.
    if( databaseFilename == NULL )
        databaseFilename = memObj->_strdup( msgstore->databaseFilename );

    OTLKCON_ENTERCRITICALSECTION(&otlkcon_sqlite_cs, "SQL> O_IProp::GetSqlObj");
    OTLKCON_ENTERCRITICALSECTION(&objectCS, "OBJ> IProp::GetSqlObj");

    //An SQLite helper object must be created per thread
    otlkcon_sqlite_helper * sqlObj = GetSqlObj();
	if( NULL == sqlObj )
	{
        OTLKCON_DEBUGBREAK;
		result = E_FAIL;
        goto create_failed;
	}
    
    sqlObj->init("O_IProp::Create", databaseFilename);

    // Partially create the entryID
    entryID = (O_ENTRYID*)memObj->pMalloc( sizeof(O_ENTRYID) );

    // If ig is GUID_NULL, then get a new guid
    if( memcmp(&ig,&otlkcon_null_guid,sizeof(MAPIUID)) == 0 )
    {
        result = NewMAPIUID( &ig );
    }

    entryID->instanceGuid = ig;
    entryID->typeGuid     = tg;
    entryID->supportedInterfaces =  OTLKCON_IMAPIPROP_CLASS | OTLKCON_IUNKNOWN_CLASS;

    result = GetTableName(ig, tg, &diskTableName);
    if( isTransacted )
    {
#ifdef OTLKCON_USE_MEM_DB
        tableName = (char *)memObj->pMalloc( 8 + strlen(diskTableName) );
        sprintf( tableName, "memDb.%s", diskTableName );
#else
        tableName = diskTableName;
#endif
    }
    else
    {
        tableName = diskTableName;
    }

    //Begin the transaction
    sqlRes = sqlObj->sqlCmdBegin();

    //Does the table already exist?
    sqlRes = sqlObj->sqlCmdSingleInt( &rowCount, 0,
        "SELECT COUNT(*) FROM sqlite_master WHERE ( type='table' ) AND\
        (name='%s');", diskTableName);
    if ( rowCount > 0 )
    {
        //Table exists
        result = MAPI_E_COLLISION;
        goto create_failed;
    }

    //Create the table
    sqlRes = sqlObj->sqlCommand("CREATE TABLE %s ( SIZE INTEGER NOT NULL, \
                                PropTag INTEGER NOT NULL,\
                                MV_SEQ INTEGER NOT NULL DEFAULT 0,\
                                TRANSACT_ID INTEGER NOT NULL DEFAULT 0,\
                                IS_REF INTEGER NOT NULL DEFAULT 0,\
                                DATA BLOB, \
            PRIMARY KEY( PropTag, MV_SEQ, TRANSACT_ID) );", diskTableName);
    if( sqlRes != S_OK )
    {
        OTLKCON_DEBUGBREAK;
        result = E_FAIL;
        goto create_failed;
    }

    // Set our version in the version table
    if( isTransacted )
    {
#ifdef OTLKCON_USE_MEM_DB
        // If transacted, create the table in memory also
        sqlRes = sqlObj->sqlCommand("CREATE TABLE %s ( SIZE INTEGER NOT NULL, \
                                    PropTag INTEGER NOT NULL,\
                                    MV_SEQ INTEGER NOT NULL DEFAULT 0,\
                                    TRANSACT_ID INTEGER NOT NULL DEFAULT 0,\
                                    IS_REF INTEGER NOT NULL DEFAULT 0,\
                                    DATA BLOB, \
                PRIMARY KEY( PropTag, MV_SEQ, TRANSACT_ID) );", tableName);
        if( sqlRes != S_OK )
        {
            OTLKCON_DEBUGBREAK;
            result = E_FAIL;
            goto create_failed;
        }
#endif // OTLKCON_USE_MEM_DB

        sqlRes = sqlObj->sqlCommand(
            "INSERT INTO object_version_table VALUES ( NULL, '%s', 1 )", 
                diskTableName );
        if( sqlRes != S_OK )
        {
            OTLKCON_DEBUGBREAK;
            result = E_FAIL;
            goto create_failed;
        }
    }

    // Some default properties may be set right here
    int propCount = 0;
    SPropValue defaultProps[4] = {0};

    //PR_OTLKCON_VALID
    //After the object is fully initialized, this is set to true.
    //Needed for failures whiling creating objects.
    defaultProps[propCount].ulPropTag       = PR_OTLKCON_VALID;
    defaultProps[propCount++].Value.b         = FALSE;

    //PR_MDB_PROVIDER
    //Our message store type ID
    defaultProps[propCount].ulPropTag       = PR_MDB_PROVIDER;
    defaultProps[propCount].Value.bin.cb  = sizeof(MAPIUID);
    defaultProps[propCount++].Value.bin.lpb = (LPBYTE)&otlkcon_mdb_provider_guid;
    
    // FIXME:  Should this be calculated instead?
    //PR_STORE_SUPPORT_MASK
    //Bitmask of capabilites for MAPI clients
    defaultProps[propCount].ulPropTag = PR_STORE_SUPPORT_MASK;
    defaultProps[propCount++].Value.l   =    STORE_CREATE_OK
                                            | STORE_ENTRYID_UNIQUE 
                                            | STORE_MODIFY_OK
                                            | STORE_RESTRICTION_OK
                                            | STORE_SORT_OK
                                            | STORE_SEARCH_OK 
                                            | STORE_NOTIFY_OK 
                                            | STORE_SUBMIT_OK
											| STORE_ATTACH_OK;

    result = _IntSetProps(propCount, (LPSPropValue)&defaultProps, NULL );

    computedProperties.insert( PR_STORE_ENTRYID );
    computedProperties.insert( PR_MAPPING_SIGNATURE );
    computedProperties.insert( PR_STORE_RECORD_KEY );

    {
		OTLKCON_ENTERCRITICALSECTION(&otlkcon_objects_cs, "IProp::Create");
		otlkcon_event_objects[entryID] = this;
		OTLKCON_LEAVECRITICALSECTION(&otlkcon_objects_cs, "IProp::Create");
	}

    // Add ourselves to the object map
    msgstore->loadedObjects[entryID] = this;

//create_end:
    sqlRes = sqlObj->sqlCmdCommit();
    
    OTLKCON_LEAVECRITICALSECTION(&objectCS, "OBJ> IProp::GetSqlObj");
    OTLKCON_LEAVECRITICALSECTION(&otlkcon_sqlite_cs, "SQL> O_IProp::GetSqlObj");
    return result;

create_failed:
    sqlRes = sqlObj->sqlCmdRollback();
    OTLKCON_LEAVECRITICALSECTION(&objectCS, "OBJ> IProp::GetSqlObj");
    OTLKCON_LEAVECRITICALSECTION(&otlkcon_sqlite_cs, "SQL> O_IProp::GetSqlObj");
    return result;
}

HRESULT O_IProp::Load( otlkcon_generic_msgstore *mstr, O_ENTRYID *eid )
{
    HRESULT result = S_OK;
    int sqlRes;
    int rowCount;
    
	msgstore = mstr;
    if( msgstore == NULL )
    {
        OTLKCON_DEBUGBREAK
        return MAPI_E_INVALID_PARAMETER;
    }
	
    if( eid == NULL )
    {
        OTLKCON_DEBUGBREAK
        return MAPI_E_INVALID_PARAMETER;
    }

    OTLKCON_ENTERCRITICALSECTION(&otlkcon_sqlite_cs, "SQL> O_IProp::Load");
    OTLKCON_ENTERCRITICALSECTION(&objectCS, "OBJ> IProp::Load");

    memObj = new otlkcon_memory( msgstore->memObj );

    databaseFilename = memObj->_strdup( msgstore->databaseFilename );

    otlkcon_sqlite_helper * sqlObj = GetSqlObj();
	if( NULL == sqlObj )
	{
        OTLKCON_DEBUGBREAK
		result = E_FAIL;
        goto load_end;
	}
    
	result = GetTableName(eid->instanceGuid, eid->typeGuid, &diskTableName);
//	result = GetTableName(ig, tg, &diskTableName);

    result = sqlObj->sqlCmdBegin();

    // Does the object exist?
    sqlRes = sqlObj->sqlCmdSingleInt( &rowCount, 0,
        "SELECT COUNT(*) FROM sqlite_master WHERE ( type='table' ) AND\
        (name='%s');", diskTableName );
    if( sqlRes != S_OK )
    {
        OTLKCON_DEBUGBREAK;
    }

	if( rowCount < 1)
	{
		//OTLKCON_DEBUGBREAK
		result = MAPI_E_NOT_FOUND;
        goto load_end;
	} // if

    cRef = 1;
    
    entryID = (O_ENTRYID*)memObj->malloc( sizeof(O_ENTRYID) );
    memcpy(entryID,eid,sizeof(O_ENTRYID));

    sqlObj->init("O_IProp::Load", databaseFilename);

    result = GetTableName(eid->instanceGuid, eid->typeGuid, &diskTableName);
    if( isTransacted )
    {
#ifdef OTLKCON_USE_MEM_DB
        tableName = (char *)memObj->pMalloc( 8 + strlen(diskTableName) );
        sprintf( tableName, "memDb.%s", diskTableName );
#else
        tableName = diskTableName;
#endif
    }
    else
    {
        tableName = diskTableName;
    }

#ifdef OTLKCON_USE_MEM_DB
    if( isTransacted )
    {
        // Is the table in memory yet?
        sqlRes = sqlObj->sqlCmdSingleInt( &rowCount, 0,
            "SELECT COUNT(*) FROM memDb.sqlite_master WHERE ( type='table' ) AND\
            (name='%s');", diskTableName );
        if( sqlRes != S_OK )
        {
            OTLKCON_DEBUGBREAK;
        }

        if( rowCount < 1 )
        {
            // If transacted, create the table in memory also
            sqlRes = sqlObj->sqlCommand("CREATE TABLE %s ( SIZE INTEGER NOT NULL, \
                                        PropTag INTEGER NOT NULL,\
                                        MV_SEQ INTEGER NOT NULL DEFAULT 0,\
                                        TRANSACT_ID INTEGER NOT NULL DEFAULT 0,\
                                        IS_REF INTEGER NOT NULL DEFAULT 0,\
                                        DATA BLOB, \
                    PRIMARY KEY( PropTag, MV_SEQ, TRANSACT_ID) );", tableName);
            if( sqlRes != S_OK )
            {
                OTLKCON_DEBUGBREAK;
                result = E_FAIL;
                goto load_end;
            }

            // Populate the table
            sqlRes = sqlObj->sqlCommand(
                "INSERT OR REPLACE INTO %s SELECT  SIZE, PropTag, MV_SEQ,\
                        0 AS TRANSACT_ID, IS_REF, DATA FROM %s", 
                                                    tableName, diskTableName );
            if( sqlRes != S_OK )
            {
                OTLKCON_DEBUGBREAK;
                result = E_FAIL;
                goto load_end;
            }
        }
    }
#endif // OTLKCON_USE_MEM_DB

    msgstore->loadedObjects[entryID] = this;

    {
		OTLKCON_ENTERCRITICALSECTION(&otlkcon_objects_cs, "IProp::Load");
		otlkcon_event_objects[entryID] = this;
		OTLKCON_LEAVECRITICALSECTION(&otlkcon_objects_cs, "IProp::Load");
	}

load_end:
    sqlRes = sqlObj->sqlCmdCommit();
    if( sqlRes != S_OK )
    {
        OTLKCON_DEBUGBREAK;
    }

    OTLKCON_LEAVECRITICALSECTION(&objectCS, "OBJ> IProp::Load");
    OTLKCON_LEAVECRITICALSECTION(&otlkcon_sqlite_cs, "SQL> O_IProp::Load");

    return result;
}

STDMETHODIMP_(otlkcon_sqlite_helper *) O_IProp::GetSqlObj()
{
    HRESULT result = S_OK;

    OTLKCON_ENTERCRITICALSECTION(&otlkcon_sqlite_cs, "SQL> O_IProp::GetSqlObj");
    OTLKCON_ENTERCRITICALSECTION(&objectCS, "OBJ> O_IProp::GetSqlObj");
	//Henry 29.12.2005 if we using Thread Local Storage
	//we can't store pointer to the objects variable
	//because of multithreading nature this variable can be overwritten
	//from another thread so we will use wrong variable.
	//Using this approach we need to hook THREAD_DETACH event for DLL
	//and free thread resources.
	//
	//conclusion: We need to use local variables for otlkcon_sqlite_helper
	//plus we don't need synchronization if we store data in TLS
	//so this method should return pointer to it
	//Does database works fine in multi threading environment ?
	//What we need to make it happy?
	//TODO: rethink that code after we know everything about DB.
	otlkcon_sqlite_helper * sqlObj = 
		(otlkcon_sqlite_helper *)TlsGetValue(otlkcon_tlsIndex);
    if( sqlObj == NULL )
    {
        sqlObj = new otlkcon_sqlite_helper( databaseFilename, memObj );
		if(NULL != sqlObj)
		{
			if(!TlsSetValue( otlkcon_tlsIndex, sqlObj )) 
			{
				OTLKCON_DEBUGBREAK;
				delete sqlObj;
				sqlObj = NULL;
				SetLastError(E_FAIL, "Failed to get database connection");
			} // if
		} // if
		else
		{
			OTLKCON_DEBUGBREAK
			SetLastError(E_FAIL, "Failed to get database connection");
		} // else
    }
    OTLKCON_LEAVECRITICALSECTION(&objectCS, "OBJ> O_IProp::GetSqlObj");
    OTLKCON_LEAVECRITICALSECTION(&otlkcon_sqlite_cs, "SQL> O_IProp::GetSqlObj");

    return sqlObj;
}

void O_IProp::Destroy()
{
    //sqlite3_close(datafile);
    free(objName);
}

// Get every property in this object
HRESULT O_IProp::Dump()
{
    HRESULT result = S_OK;
    char *queryStr;
    int errFlag = 0, dataSize = 0, sqlRes = 0, currTag=0, tmpBinSize=0;
    const char **queryRemainder = NULL;
    sqlite3_stmt * queryStmt = NULL;
    char *outputTableName;
    O_ITable *tbl;
    vector<ULONG> tags;

    char *strBuff;
    int strBuffSize = OTLKCON_LOG_MSG_MAX_LEN*10 - 1;
    strBuff = (char *)memObj->malloc( OTLKCON_LOG_MSG_MAX_LEN * 10);

    result = otlkcon_obj2xml_begin(&strBuff, OTLKCON_LOG_MSG_MAX_LEN*10 - 1);

    OTLKCON_ENTERCRITICALSECTION(&otlkcon_sqlite_cs, "SQL> O_IProp::Dump");

    otlkcon_sqlite_helper * sqlObj = GetSqlObj();

    sqlRes = sqlObj->sqlCmdBegin();

    // Get all the properties
    sqlRes = sqlObj->buildQuery( &queryStr,
                     "SELECT DISTINCT propTag FROM %s", 
                     tableName );

    tags.clear();
    sqlite3_prepare( sqlObj->databaseHandle, queryStr, -1, 
                                &queryStmt, queryRemainder);

    while( (sqlRes = sqlite3_step(queryStmt)) == SQLITE_ROW )
    {
        tags.push_back( sqlite3_column_int(queryStmt,0) );
    }
    
	sqlRes = sqlite3_finalize(queryStmt);
    if ( sqlRes != SQLITE_OK )
    {
        OTLKCON_LOG_PRINTF(OTLKCON_LOG_LEVEL_INFO, OTLKCON_LOG_TARGET_ALL, 
            L"IProp::Dump sqlite3_finalize error: %S", 
            sqlite3_errmsg(sqlObj->databaseHandle));
        result = E_FAIL;
        goto dump_end;
    }

    sqlRes = sqlObj->sqlCmdCommit();

    for( ULONG i=0; i<tags.size(); i++ )
    {
        currTag = tags[i];

        if( currTag == PR_CONTAINER_HIERARCHY ||
			currTag == PR_CONTAINER_CONTENTS  ||
			currTag == PR_FOLDER_ASSOCIATED_CONTENTS)
        {
            // Treat this like a IMAPITable
            result = OpenProperty(currTag, &IID_IMAPITable, 0, 0, 
						(LPUNKNOWN *)&tbl);
            switch(currTag)
            {
                case PR_CONTAINER_HIERARCHY:
                    outputTableName = memObj->_strdup("PR_CONTAINER_HIERARCHY");
                    break;
                case PR_CONTAINER_CONTENTS:
                    outputTableName = memObj->_strdup("PR_CONTAINER_CONTENTS");
                    break;
				case PR_FOLDER_ASSOCIATED_CONTENTS:
					outputTableName = memObj->_strdup("PR_FOLDER_ASSOCIATED_CONTENTS");
					break;
                default:
                    outputTableName = memObj->_strdup("ANON");
                    break;
            }
            result = otlkcon_table2xml((LPMAPITABLE)tbl, NULL, outputTableName, 
                OTLKCON_OBJ2XML_SHOW_BINARY, 0, 
                memObj->lpAllocateBuffer, memObj->lpAllocateMore, memObj->lpFreeBuffer, 
                &strBuff, strBuffSize);
        }
        else if( PROP_TYPE(currTag) == PT_OBJECT )
        {
            // use openproperty()
            // O_IStream *tempStream;
            // result = OpenProperty(currTag, &IID_IStream, 0, 
            //                0, (LPUNKNOWN *)&tempStream);

            // FIXME: A generic object to be displayed???
            //OTLKCON_DEBUGBREAK;
        }
        else
        {
            SPropTagArray propTag;
            propTag.aulPropTag[0] = currTag;
            propTag.cValues=1;
            ULONG count=0;
            LPSPropValue prop;
            result = GetProps((LPSPropTagArray)&propTag,0,&count,&prop);
            sprintf_s(strBuff+strlen(strBuff), 
                strBuffSize-strlen(strBuff), "<object>\n");
            result = otlkcon_propValue2xml(prop, OTLKCON_OBJ2XML_SHOW_BINARY, 0,
                                memObj->lpAllocateBuffer, memObj->lpAllocateMore, memObj->lpFreeBuffer, 
                                &strBuff, strBuffSize);
            sprintf_s(strBuff+strlen(strBuff), 
                strBuffSize-strlen(strBuff), "</object>\n");
        }
    }

    result = otlkcon_obj2xml_end(&strBuff, OTLKCON_LOG_MSG_MAX_LEN-1);
    OTLKCON_LOG_PRINTF(OTLKCON_LOG_LEVEL_INFO, OTLKCON_LOG_TARGET_ALL|OTLKCON_LOG_FLAG_NO_DATE, L"%S", strBuff);

    memObj->free(queryStr);
    memObj->free( strBuff );

dump_end:
    OTLKCON_LEAVECRITICALSECTION(&otlkcon_sqlite_cs, "SQL> O_IProp::Dump");
    return result;
}

STDMETHODIMP O_IProp::QueryInterface (REFIID riid, void **ppv)
{
    HRESULT result = S_OK;
    int found = 0;

    OTLKCON_LOG_PRINTF(OTLKCON_LOG_LEVEL_INFO,  OTLKCON_LOG_TARGET_ALL, L"inside %S->%S::QueryInterface. \
 {%0.8X-%0.4X-%0.4X-%0.2X%0.2X-%0.2X%0.2X%0.2X%0.2X%0.2X%0.2X}\n",objName,className,
        riid.Data1, riid.Data2, riid.Data3, riid.Data4[0], riid.Data4[1],
        riid.Data4[2], riid.Data4[3], riid.Data4[4], riid.Data4[5],
        riid.Data4[6], riid.Data4[7]);

    *ppv = NULL;

    if( !memcmp(&riid, &IID_IUnknown, sizeof(IID)) &&
            entryID->supportedInterfaces & OTLKCON_IUNKNOWN_CLASS )
    {
        found = 1;
    }
    else if( !memcmp(&riid, &IID_IMAPIProp, sizeof(IID)) &&
        entryID->supportedInterfaces & OTLKCON_IMAPIPROP_CLASS )
    {
        found = 1;
    }
    else if( !memcmp(&riid, &IID_IMessage, sizeof(IID)) &&
        entryID->supportedInterfaces & OTLKCON_IMESSAGE_CLASS )
    {
        found = 1;
    }
    else if( !memcmp(&riid, &IID_IMAPIContainer, sizeof(IID)) &&
        entryID->supportedInterfaces & OTLKCON_IMAPICONTAINER_CLASS )
    {
        found = 1;
    }
    else if( !memcmp(&riid, &IID_IMAPIFolder, sizeof(IID)) &&
        entryID->supportedInterfaces & OTLKCON_IMAPIFOLDER_CLASS )
    {
        found = 1;
    }
    else if( !memcmp(&riid, &IID_IMsgStore, sizeof(IID)) &&
        entryID->supportedInterfaces & OTLKCON_IMSGSTORE_CLASS )
    {
        found = 1;
    }
    else if( !memcmp(&riid, &IID_IMSLogon, sizeof(IID)) &&
        entryID->supportedInterfaces & OTLKCON_IMSLOGON_CLASS )
    {
        found = 1;
    }
	else if( !memcmp(&riid, &IID_IAttachment, sizeof(IID)) &&
		entryID->supportedInterfaces & OTLKCON_IATTACHMENT_CLASS )
	{
		found = 1;
	}

    if( !found )
    { 
        result = ResultFromScode(E_NOINTERFACE);
        return result;
    }

    AddRef();
    *ppv = (LPVOID)this;

    OTLKCON_LOG_PRINTF(OTLKCON_LOG_LEVEL_INFO,  OTLKCON_LOG_TARGET_ALL, L"%S->%S::QueryInterface. \
{%0.8X-%0.4X-%0.4X-%0.2X%0.2X-%0.2X%0.2X%0.2X%0.2X%0.2X%0.2X} - Supported\n",
    objName, className,
    riid.Data1, riid.Data2, riid.Data3, riid.Data4[0], riid.Data4[1],
    riid.Data4[2], riid.Data4[3], riid.Data4[4], riid.Data4[5],
    riid.Data4[6], riid.Data4[7]);

    return result;
}

ULONG STDMETHODCALLTYPE O_IProp::AddRef (void)
{
    cRef++;
    OTLKCON_LOG_PRINTF(OTLKCON_LOG_LEVEL_INFO,  OTLKCON_LOG_TARGET_ALL, L"%S->%S::AddRef %d\n",
            objName, className, cRef);

    return cRef;
}

ULONG STDMETHODCALLTYPE O_IProp::Release(void)
{
    OTLKCON_LOG_PRINTF(OTLKCON_LOG_LEVEL_INFO,  OTLKCON_LOG_TARGET_ALL, L"%S->%S::Release %d\n",
            objName, className, cRef);

    if ( cRef == 1 )
    {
		// Henry 20.11.2005
		// I think it is better to save transacted data in memory an insert it in SaveChanges
		// TODO: More than that new message should be deleted if it was not properly saved

        //Delete all pending transacted changes
        if( isTransacted )
        {
            OTLKCON_ENTERCRITICALSECTION(&otlkcon_sqlite_cs, "SQL> O_IProp::Release");
			otlkcon_sqlite_helper * sqlObj = GetSqlObj();
			if(NULL == sqlObj)
			{
				OTLKCON_DEBUGBREAK;
			} // if
			else
			{
                // Test the the table exists first.
                int rowCount = 0; 
                int sqlRes = sqlObj->sqlCmdSingleInt( &rowCount, 0,
                    "SELECT COUNT(*) FROM sqlite_master WHERE ( type='table' ) AND\
                    (name='%s');", tableName);
                if ( rowCount > 0 )
                {
                    //Table exists
                    sqlRes = sqlObj->sqlCommand( 
					    "DELETE FROM %s WHERE TRANSACT_ID = %d",
					    tableName, objNum);
				    if( sqlRes != S_OK )
				    {
					    OTLKCON_DEBUGBREAK;
				    }
                }
				
			} // else
            OTLKCON_LEAVECRITICALSECTION(&otlkcon_sqlite_cs, "SQL> O_IProp::GetSqlObj");
        }

        return 0;
    }

    return --cRef;
}

HRESULT O_IProp::SetLastError(HRESULT errID, char *errStr)
{
    HRESULT result = S_OK;

    lastErrorMsgID = errID;

    if( errID == S_OK )
    {
        strcpy_s(lastErrorMsg, OTLKCON_GETLASTERROR_MSG_SIZE,
            OTLKCON_SUCCESS_ERROR_STR);
    }
    else if( strlen(errStr) )
    {
        strcpy_s( lastErrorMsg, OTLKCON_GETLASTERROR_MSG_SIZE,
            errStr );
    }
    else
    {
        strcpy_s( lastErrorMsg, OTLKCON_GETLASTERROR_MSG_SIZE,
            "Unspecified error" );
    }

    lastErrorMsgID = errID;

    return result;
}

STDMETHODIMP O_IProp::GetLastError(HRESULT hResult,
  ULONG ulFlags,
  LPMAPIERROR *lppMAPIError  
)
{
    HRESULT result = S_OK;
    char *errMsg = NULL;

    if( ulFlags & MAPI_UNICODE )
    {
        return MAPI_E_BAD_CHARWIDTH;
    }

    memObj->lpAllocateBuffer(sizeof(MAPIERROR), (LPVOID*)lppMAPIError);
    memset(*lppMAPIError,0,sizeof(MAPIERROR));

    memObj->lpAllocateMore(OTLKCON_GETLASTERROR_MSG_SIZE, (LPVOID)*lppMAPIError, 
                                                            (LPVOID*)&errMsg);
    memset(errMsg,0,OTLKCON_GETLASTERROR_MSG_SIZE);

    if( objName && strlen(className) )
    {
        sprintf_s(errMsg, OTLKCON_GETLASTERROR_MSG_SIZE, "%s : ", className);
    }
    else
    {
        strcpy_s(errMsg, OTLKCON_GETLASTERROR_MSG_SIZE, "<Unnamed object>::");
    }

    if( lastCalledFunctionID == 0 )
    {
        strcat_s(errMsg, OTLKCON_GETLASTERROR_MSG_SIZE,
                    "<Undefined function>");
    }
    else
    {
        strcat_s(errMsg, OTLKCON_GETLASTERROR_MSG_SIZE,
                    lastCalledFunctionName);
    }

    _snprintf_s(errMsg+strlen(errMsg),
        OTLKCON_GETLASTERROR_MSG_SIZE-strlen(errMsg),
        OTLKCON_GETLASTERROR_MSG_SIZE-strlen(errMsg)-1,
                    " :0x%0.8X:%s", lastErrorMsgID, lastErrorMsg);

    (*lppMAPIError)->ulVersion = MAPI_ERROR_VERSION;
    (*lppMAPIError)->lpszError = errMsg;
    memObj->lpAllocateMore((ULONG)strlen("OpenConnector Message Store")+1,
                                        (LPVOID)*lppMAPIError,(LPVOID*)&errMsg);
    strcpy_s(errMsg, strlen("OpenConnector Message Store")+1,
        "OpenConnector Message Store");
    (*lppMAPIError)->lpszComponent = errMsg;

    return result;
}

STDMETHODIMP O_IProp::SaveChanges( ULONG ulFlags )
{
    HRESULT result = S_OK;
    int currVersion, sqlRes;

    //Validate_IMAPIProp_SaveChanges(this, ulFlags);

    if( ulFlags & MAPI_DEFERRED_ERRORS )
    {
        OTLKCON_LOG_PRINTF(OTLKCON_LOG_LEVEL_INFO,  OTLKCON_LOG_TARGET_ALL, 
            L"O_IProp::SaveChanges() : MAPI_DEFERRED_ERRORS set.\n" );
    }
    if( ulFlags & KEEP_OPEN_READWRITE )
    {
        OTLKCON_LOG_PRINTF(OTLKCON_LOG_LEVEL_INFO,  OTLKCON_LOG_TARGET_ALL, 
            L"O_IProp::SaveChanges() : KEEP_OPEN_READWRITE set.\n" );
    }
    if( ulFlags & KEEP_OPEN_READONLY )
    {
        OTLKCON_LOG_PRINTF(OTLKCON_LOG_LEVEL_INFO,  OTLKCON_LOG_TARGET_ALL, 
            L"O_IProp::SaveChanges() : KEEP_OPEN_READONLY set.\n" );
    }
    if( ulFlags & FORCE_SAVE )
    {
        OTLKCON_LOG_PRINTF(OTLKCON_LOG_LEVEL_INFO,  OTLKCON_LOG_TARGET_ALL, 
            L"O_IProp::SaveChanges() : FORCE_SAVE set.\n" );
    }

	//Henry 30.11.2005
	//TODO
	//We need to modify access for this object
	//according to passed ulFlags

    if( !isTransacted )
        return S_OK;

    OTLKCON_ENTERCRITICALSECTION(&otlkcon_sqlite_cs, "SQL> O_IProp::SaveChanges");
    OTLKCON_ENTERCRITICALSECTION(&objectCS, "OBJ> O_IProp::SaveChanges");

	otlkcon_sqlite_helper * sqlObj = GetSqlObj();
    	
    result = sqlObj->sqlCmdBegin();
	if(FAILED(result))
	{
		goto LeaveWithErrorNoDB;
	} // if

	sqlRes = sqlObj->sqlCmdSingleInt( &currVersion, 0,
        "SELECT version FROM object_version_table \
            WHERE name = '%s'", diskTableName);

    if ( sqlRes != S_OK )
    {
        OTLKCON_DEBUGBREAK;
        result = E_FAIL;
		goto LeaveWithErrorNoDB;
    }

    if( currVersion != tblVersion )
    {
        // The table version has changed
        OTLKCON_LOG_PRINTF(OTLKCON_LOG_LEVEL_INFO,  OTLKCON_LOG_TARGET_ALL, 
            L"O_IProp::SaveChanges() : Warning : currVersion is %d \
                but tblVersion is %d.\n", currVersion, tblVersion );
        if( currVersion < tblVersion )
        {
            // This should never happen
            OTLKCON_DEBUGBREAK;
        }
        if( !(ulFlags & FORCE_SAVE) )
        {
			result = MAPI_E_OBJECT_CHANGED;
			goto LeaveWithErrorNoDB;
        }
    }

    // Delete the 'deleted' rows
//    char * propName;
    for(set <ULONG>::iterator j = deletedProperties.begin(); 
                                j!=deletedProperties.end(); j++)
    {
        //propName = msgstore->prop2str->get(*j);
        //if(propName)
        //{
        //    OTLKCON_LOG_PRINTF(OTLKCON_LOG_LEVEL_INFO,  OTLKCON_LOG_TARGET_ALL, 
        //        L"<%S> %S->O_IProp::SaveChanges() : Deleting 0x%0.8X ( %S )\n", 
        //            objName, className, *j, propName);
        //    memObj->lpFreeBuffer(propName);
        //}
        //else
        //    OTLKCON_LOG_PRINTF(OTLKCON_LOG_LEVEL_INFO,  OTLKCON_LOG_TARGET_ALL, 
        //        L"<%S> %S->O_IProp::SaveChanges() : Deleting 0x%0.8X\n", 
        //        objName, className, *j);

        sqlRes = sqlObj->sqlCommand( 
                "DELETE FROM %s WHERE ( PropTag >> 16 ) = %d",
                tableName, *j);
        if( sqlRes != S_OK )
        {
            OTLKCON_DEBUGBREAK;
            result = E_FAIL;
			goto LeaveWithError;
        }
    }

    // Walk through the table, copy the shadow rows to the
    // real rows.
#ifdef OTLKCON_DEBUG
    //const char **queryRemainder = NULL;
    //sqlite3_stmt * queryStmt = NULL;
    //char *queryStr;
    //ULONG currTag;

    //sqlRes = sqlObj->buildQuery( &queryStr,
    //                "SELECT  PropTag FROM %s \
    //                    WHERE TRANSACT_ID = %d", tableName, objNum);
    //sqlite3_prepare( sqlObj->databaseHandle, queryStr, -1, 
    //                            &queryStmt, queryRemainder);
    //
    ////while( (sqlRes = sqlite3_step(queryStmt)) == SQLITE_ROW )
    ////{
    ////    currTag = sqlite3_column_int(queryStmt, 0);
    ////    propName = msgstore->prop2str->get(currTag);
    ////    if(propName)
    ////    {
    ////        OTLKCON_LOG_PRINTF(OTLKCON_LOG_LEVEL_INFO,  OTLKCON_LOG_TARGET_ALL, 
    ////            L"<%S> %S->O_IProp::SaveChanges() : Altering 0x%0.8X ( %S )\n", 
    ////                objName, className, currTag, propName);
    ////        memObj->lpFreeBuffer(propName);
    ////    }
    ////    else
    ////        OTLKCON_LOG_PRINTF(OTLKCON_LOG_LEVEL_INFO,  OTLKCON_LOG_TARGET_ALL, 
    ////            L"<%S> %S->O_IProp::SaveChanges() : Altering 0x%0.8X\n", 
    ////            objName, className, currTag);
    ////}

    //sqlRes = sqlite3_finalize(queryStmt);
    //if ( sqlRes != SQLITE_OK )
    //{
    //    OTLKCON_DEBUGBREAK;
    //}

#endif // OTLKCON_DEBUG

    // Henry: 20.11.2005
	// We need to make SaveChanges inside transaction

    // Copy to disk...
    sqlRes = sqlObj->sqlCommand(
                    "INSERT OR REPLACE INTO %s SELECT  SIZE, PropTag, MV_SEQ,\
                        0 AS TRANSACT_ID, IS_REF, DATA FROM %s \
                        WHERE TRANSACT_ID = %d", diskTableName, tableName, objNum);
    if( sqlRes != S_OK )
    {
        OTLKCON_DEBUGBREAK;
        result = E_FAIL;
		goto LeaveWithError;
        //return result;
    }

#ifdef OTLKCON_USE_MEM_DB
    // Save in memory...
	sqlRes = sqlObj->sqlCommand(
                    "INSERT OR REPLACE INTO %s SELECT  SIZE, PropTag, MV_SEQ,\
                        0 AS TRANSACT_ID, IS_REF, DATA FROM %s \
                        WHERE TRANSACT_ID = %d", tableName, tableName, objNum);
    if( sqlRes != S_OK )
    {
        OTLKCON_DEBUGBREAK;
        result = E_FAIL;
		goto LeaveWithError;
        //return result;
    }
#endif // OTLKCON_USE_MEM_DB

    // Delete the shadow rows...
    sqlRes = sqlObj->sqlCommand( 
                    "DELETE FROM %s WHERE TRANSACT_ID = %d",
                    tableName, objNum);
    if( sqlRes != S_OK )
    {
        OTLKCON_DEBUGBREAK;
        result = E_FAIL;
		goto LeaveWithError;
        // return result;
    }

    // Increment the table version...
    sqlRes = sqlObj->sqlCommand(
                    "UPDATE object_version_table SET version = version + 1\
                    WHERE name = '%s'", diskTableName );
    if( sqlRes != S_OK )
    {
        OTLKCON_DEBUGBREAK;
        result = E_FAIL;
        // return result;
		goto LeaveWithError;
    }
	
    tblVersion = 0;
	
LeaveWithError:
	if(SUCCEEDED(result))
	{
		result = sqlObj->sqlCmdCommit();
		if(SUCCEEDED(result))
		{
			deletedProperties.clear();
		} // if
		else
		{
			sqlObj->sqlCmdRollback();
		} // else
	} // if
	else
	{
		sqlObj->sqlCmdRollback();
	} // else

LeaveWithErrorNoDB:
    OTLKCON_LEAVECRITICALSECTION(&objectCS, "O_IProp::SaveChanges");
    OTLKCON_LEAVECRITICALSECTION(&otlkcon_sqlite_cs, "SQL> O_IProp::SaveChanges");

    return result;
}

STDMETHODIMP O_IProp::GetProps(LPSPropTagArray lpPropTagArray,
                      ULONG        ulFlags,
                      ULONG        *lpcValues,
                      LPSPropValue *lppPropArray
)
{
    HRESULT result = S_OK;

	result = _IntGetProps( lpPropTagArray, ulFlags, lpcValues,
                      lppPropArray );
    if( FAILED(result) )
    {
        OTLKCON_DEBUGBREAK;
    }

    LPSPropValue currProp;
    SBinary *tmpEntryID;
    HRESULT res;
    LPENTRYID tmpEntryID2;
    LPMAPIUID tmpUID;

	int errors = 0;

    for( ULONG i=0; i<*lpcValues; i++)
    {
        currProp = (*lppPropArray+i);

        if(PT_ERROR == PROP_TYPE(currProp->ulPropTag)) 
        {
			++errors;
        } // if

		switch(PROP_ID(currProp->ulPropTag))
        {
			case PROP_ID(PR_ENTRYID):
            case PROP_ID(PR_STORE_ENTRYID):
                if( PROP_ID(currProp->ulPropTag) == PROP_ID(PR_STORE_ENTRYID)
                  || entryID->typeID == MAPI_STORE )
                {
                    //This tag needs to be wrapped
                    //used to open message store and related objects.
                    tmpEntryID = (SBinary*)memObj->malloc( sizeof(SBinary) );
                    tmpEntryID2 = (LPENTRYID)memObj->malloc( sizeof(O_ENTRYID) );
                    memcpy(tmpEntryID2,msgstore->entryID,sizeof(O_ENTRYID));
                    
					res = msgstore->lpMAPISup->WrapStoreEntryID(
						//0, OTLKCON_MSTORE_DLL_NAME, 
                        sizeof(O_ENTRYID), tmpEntryID2,
                                    &(tmpEntryID->cb), (LPENTRYID*)&(tmpEntryID->lpb));
                    currProp->Value.bin.lpb = tmpEntryID->lpb;
                    currProp->Value.bin.cb  = tmpEntryID->cb;
					if(PT_ERROR == PROP_TYPE(currProp->ulPropTag))
					{
						--errors;
					} // if
					currProp->ulPropTag = PROP_TAG(PT_BINARY, PROP_ID(currProp->ulPropTag));
                }
				else
				{
					errors = errors;
				} // else
                break;

            case PROP_ID(PR_MAPPING_SIGNATURE):
                currProp->Value.bin.lpb = (LPBYTE)memObj->malloc( sizeof(O_ENTRYID) );
                memcpy(currProp->Value.bin.lpb, msgstore->entryID, sizeof(O_ENTRYID));
                currProp->Value.bin.cb = sizeof(O_ENTRYID);
				if(PT_ERROR == PROP_TYPE(currProp->ulPropTag)) 
				{
					--errors;
				} // if
				currProp->ulPropTag = PR_MAPPING_SIGNATURE;
                break;

            case PROP_ID(PR_STORE_RECORD_KEY):
                tmpUID = (LPMAPIUID)memObj->malloc( sizeof(MAPIUID) );
                memcpy(tmpUID, &msgstore->entryID->instanceGuid, sizeof(MAPIUID));
                currProp->Value.bin.lpb = (LPBYTE)tmpUID;
                currProp->Value.bin.cb = sizeof(MAPIUID);
				if(PT_ERROR == PROP_TYPE(currProp->ulPropTag)) 
				{
					--errors;
				} // if
				currProp->ulPropTag = PR_STORE_RECORD_KEY;
                break;
			case PROP_ID(PR_INSTANCE_KEY):
				tmpUID = (LPMAPIUID)memObj->mallocMore( *lppPropArray, sizeof(MAPIUID) );
				memcpy(tmpUID, &entryID->instanceGuid, sizeof(MAPIUID));
				currProp->ulPropTag = PR_INSTANCE_KEY;
				currProp->Value.bin.lpb = (LPBYTE)tmpUID;
				currProp->Value.bin.cb = sizeof(MAPIUID);
				if(PT_ERROR == PROP_TYPE(currProp->ulPropTag)) 
				{
					--errors;
				} // if
				currProp->ulPropTag = PR_INSTANCE_KEY;
				break;
			case PROP_ID(PR_STORE_SUPPORT_MASK):
				if(PT_ERROR == PROP_TYPE(currProp->ulPropTag)) 
				{
					--errors;
				} // if
				currProp->ulPropTag = PR_STORE_SUPPORT_MASK;
				currProp->Value.l  = STORE_CREATE_OK
									| STORE_ENTRYID_UNIQUE 
									| STORE_MODIFY_OK
									| STORE_RESTRICTION_OK
									| STORE_SORT_OK
									| STORE_SEARCH_OK 
									| STORE_NOTIFY_OK 
									| STORE_SUBMIT_OK
									| STORE_ATTACH_OK;
				break;
        }
    }

	TRACE_PROPVALUES(OTLKCON_LOG_TARGET_ALL,
		*lppPropArray, *lpcValues, L"O_IProp::GetProps retrieved props");
	if(0 != errors)
	{
		result = MAPI_W_ERRORS_RETURNED;
	} // if	
	else
	{
		result = S_OK;
	} // else
    return result;
}

STDMETHODIMP O_IProp::_QueryValue(sqlite3_stmt *queryStmt, LPSPropValue lpPropValue, LPVOID lpParentObject)
{
	HRESULT hResult = E_INVALIDARG;
	int tmpBinSize = 0;
	void* data = NULL;
	void* tmpBin = NULL;
	void* tmpSQLBin = NULL;

	if(NULL != queryStmt && NULL != lpPropValue)
	{
		hResult = S_OK;
		switch( PROP_TYPE(lpPropValue->ulPropTag) )
		{
		case PT_I2:
			// short i 
			lpPropValue->Value.i
				= sqlite3_column_int(queryStmt, OTLKCON_IPROP_TABLE_DATA_COL);
			break;

		case PT_LONG:
			// long l or ul??
			lpPropValue->Value.l 
				= sqlite3_column_int(queryStmt, OTLKCON_IPROP_TABLE_DATA_COL);
			break;
		case PT_BOOLEAN:
			// long l or ul??
			lpPropValue->Value.b 
				= sqlite3_column_int(queryStmt, OTLKCON_IPROP_TABLE_DATA_COL);
			break;

		case PT_STRING8:
			// LPSTR lpszA 
			// How do you free this memory??
			data = (unsigned char *)sqlite3_column_text(queryStmt, 
				OTLKCON_IPROP_TABLE_DATA_COL);
			lpPropValue->Value.lpszA = memObj->_strdupMore( lpParentObject, (const char *)data );
			break;
		case PT_UNICODE:
			// LPSTR lpszW
			// How do you free this memory??
			data = (unsigned char *)sqlite3_column_text16(queryStmt, 
				OTLKCON_IPROP_TABLE_DATA_COL);
			lpPropValue->Value.lpszW = memObj->w_strdupMore(lpParentObject, (const wchar_t *)data );
			break;

		case PT_BINARY:
			// SBinary bin
			tmpBinSize = sqlite3_column_bytes(queryStmt, 
				OTLKCON_IPROP_TABLE_DATA_COL);
			memObj->lpAllocateMore(tmpBinSize+1, lpParentObject, (LPVOID*)&tmpBin);
			memset(tmpBin, 0, tmpBinSize+1);
			tmpSQLBin = (LPBYTE)sqlite3_column_blob(queryStmt, 
				OTLKCON_IPROP_TABLE_DATA_COL);
			memcpy(tmpBin, tmpSQLBin, tmpBinSize);
			lpPropValue->Value.bin.lpb = (LPBYTE)tmpBin;
			lpPropValue->Value.bin.cb  = tmpBinSize;
			break;

		case PT_CLSID:
			// LPGUID lpguid
			tmpBinSize = sqlite3_column_bytes(queryStmt, 
				OTLKCON_IPROP_TABLE_DATA_COL);
			if( tmpBinSize != 16 )
			{
				OTLKCON_DEBUGBREAK;
				hResult = E_FAIL;
			}

			memObj->lpAllocateMore(tmpBinSize+1, lpParentObject, (LPVOID*)&tmpBin);
			memset(tmpBin, 0, tmpBinSize+1);
			tmpSQLBin = (LPBYTE)sqlite3_column_blob(queryStmt, 
				OTLKCON_IPROP_TABLE_DATA_COL);
			memcpy(tmpBin, tmpSQLBin, tmpBinSize);
			lpPropValue->Value.lpguid = (LPGUID)tmpBin;
			break;

		case PT_OBJECT:
			// According to my interpretion of the docs we 
			// shouldn't do this since PT_OBJECTs need to be
			// accessed by OpenProperty(), but somoe clients
			// use HrGetProps() to open table properties
			// anyway.  Hence, take care of that.

			lpPropValue->Value.x = PR_NULL;

			/*// Open properties we are familiar with.
			switch( lpPropTagArray->aulPropTag[i] )
			{
			case PR_CONTAINER_HIERARCHY:
			case PR_CONTAINER_CONTENTS:
			result = OpenProperty(lpPropTagArray->aulPropTag[i], 
			&IID_IMAPITable, 0, MAPI_MODIFY, 
			(LPUNKNOWN *)&tmpBin);
			if( result != S_OK )
			{
			// OpenProperty() failed
			OTLKCON_DEBUGBREAK;
			}
			// FIXME: 64-bit pointer truncated warning.
			// MAPI needs to be updated; Forces pointers
			// to be stored in a 'LONG' in SPropValue.Value
			#pragma warning ( disable: 4311 )
			(*lppPropArray)[i].Value.x = (LONG)tmpBin;
			#pragma warning ( default: 4311 )
			t = (O_ITable*)tmpBin;
			t->Dump(0);
			t->GetRowCount(0, (ULONG*)&rowCount);
			break;

			default:
			// A PT_OBJECT we do not know how to open
			OTLKCON_LOG_PRINTF(OTLKCON_LOG_LEVEL_INFO, OTLKCON_LOG_TARGET_ALL, 
			L"IProp::GetProps unknown PT_OBJECT: 0x%0.8X", 
			lpPropTagArray->aulPropTag[i] );
			OTLKCON_DEBUGBREAK;
			break;
			}*/
			break;

		case PT_SYSTIME:
			{
				FILETIME tempFT = {0};
				tmpBinSize = sqlite3_column_bytes(queryStmt, 
					OTLKCON_IPROP_TABLE_DATA_COL);
				if( tmpBinSize != sizeof(FILETIME) )
				{
					hResult = E_FAIL;
					OTLKCON_DEBUGBREAK;
				}

				tmpSQLBin = (LPBYTE)sqlite3_column_blob(queryStmt, 
					OTLKCON_IPROP_TABLE_DATA_COL);
				memcpy(&tempFT, tmpSQLBin, sizeof(FILETIME));
				lpPropValue->Value.ft = tempFT;
			}
			break;

		default:
			OTLKCON_LOG_PRINTF(OTLKCON_LOG_LEVEL_INFO, OTLKCON_LOG_TARGET_ALL, 
				L"O_IProp::_QueryValue unknown type: 0x%0.8X",
				PROP_TYPE(lpPropValue->ulPropTag) );
			OTLKCON_DEBUGBREAK
			hResult = E_FAIL;
		}
	} // if valid parameters
	return hResult;
}

STDMETHODIMP O_IProp::_QueryOneProp(ULONG ulPropTag, LPSPropValue* lppPropValue)
{
	HRESULT hResult = E_INVALIDARG;
	if(NULL != lppPropValue)
	{
		*lppPropValue = NULL;
		//Refresh the database handle if needed
		otlkcon_sqlite_helper * sqlObj = GetSqlObj();
		if(NULL != sqlObj)
		{
			hResult = sqlObj->sqlCmdBegin();
			if(SUCCEEDED(hResult))
			{
				//Allocate enough memory for the requested properties
				if( memObj->lpAllocateBuffer(sizeof(SPropValue), 
					(LPVOID*)lppPropValue) != S_OK )
				{
					hResult = MAPI_E_NOT_ENOUGH_MEMORY;
					OTLKCON_DEBUGBREAK;
				}
				memset(*lppPropValue, 0, sizeof(SPropValue));
				
				if(SUCCEEDED(hResult))
				{
					const char *queryRemainder = NULL;
					char *queryStr = NULL;
					
					HRESULT sqlRes = sqlObj->buildQuery( &queryStr,
						"SELECT * FROM %s WHERE %s = %d \
						AND TRANSACT_ID = 0 \
						ORDER BY TRANSACT_ID DESC \
						LIMIT 1", diskTableName, OTLKCON_REGULAR_PROPTAG_STR,
						ulPropTag);
					
					sqlite3_stmt * queryStmt = NULL;
					sqlRes = sqlite3_prepare( sqlObj->databaseHandle, queryStr, -1, 
						&queryStmt, &queryRemainder);
					
					sqlRes = sqlite3_step(queryStmt);
					if(SQLITE_ROW == sqlRes)
					{
						//we have property
						(*lppPropValue)->ulPropTag = ulPropTag;
						hResult = _QueryValue(queryStmt, *lppPropValue, *lppPropValue);
					} // if
					else
					{
						hResult = MAPI_E_NOT_FOUND;
					} // else
					sqlRes = sqlite3_finalize(queryStmt);
					memObj->free(queryStr);
				} // if memory allocated
				if(FAILED(hResult))
				{
					memObj->free(*lppPropValue);
					*lppPropValue = NULL;
				} // if
				sqlObj->sqlCmdRollback();
			} // if
		} // if database valid
		else
		{
			hResult = E_FAIL;
		} // else
	} // if valid args
	return hResult;
}

STDMETHODIMP O_IProp::_IntGetProps(LPSPropTagArray lpPropTagArray,
                      ULONG        ulFlags,
                      ULONG        *lpcValues,
                      LPSPropValue *lppPropArray
)
{
    HRESULT result = S_OK;
    int errFlag = 0, sqlRes = 0, rowCount = 0, tmpBinSize=0;
    ULONG currTag=0;
    size_t dataSize = 0;
    unsigned char *data;
    sqlite3_stmt *queryStmt_regular = NULL,
                 *queryStmt_unspecified = NULL,
                 *queryStmt = NULL;
    LPBYTE tmpBin, tmpSQLBin;
    int found;
    FILETIME tempFT = {0};
    char buff[512] = {0};

    //Validate_IMAPIProp_GetProps(this,lpPropTagArray,ulFlags,
    //                                                lpcValues,lppPropArray);
    lastCalledFunctionID = OTLKCON_IPROP_GETPROPS;
    strcpy_s(lastCalledFunctionName, OTLKCON_GETLASTERROR_FUNCTION_NAME_SIZE+1,
        OTLKCON_IPROP_GETPROPS_STR);
    result = SetLastError(S_OK, "");

    if( ulFlags & MAPI_UNICODE )
    {
        SetLastError(MAPI_E_BAD_CHARWIDTH, "Unicode is not supported");
        return MAPI_E_BAD_CHARWIDTH;
    }

    OTLKCON_ENTERCRITICALSECTION(&otlkcon_sqlite_cs, "SQL> O_IProp::GetProps");
    OTLKCON_ENTERCRITICALSECTION(&objectCS, "OBJ> IProp::GetProps");

    //Refresh the database handle if needed
	otlkcon_sqlite_helper * sqlObj = GetSqlObj();
	if(NULL == sqlObj)
	{
		result = E_FAIL;
        goto intgetprops_failed;
	} // if

    result = sqlObj->sqlCmdBegin();
	if(FAILED(result))
	{
		OTLKCON_DEBUGBREAK;
	} // if

    if( lpPropTagArray == NULL )
    {
        result = GetPropList(0, &lpPropTagArray);
        if( FAILED(result) )
        {
            OTLKCON_DEBUGBREAK
            SetLastError(result, "GetPropList failed");
            goto intgetprops_failed;
        }
    }

    *lpcValues = lpPropTagArray->cValues;

    //Allocate enough memory for the requested properties
    if( memObj->lpAllocateBuffer(lpPropTagArray->cValues * sizeof(SPropValue), 
        (LPVOID*)lppPropArray) != S_OK )
    {
        OTLKCON_DEBUGBREAK;
    }
    memset(*lppPropArray, 0, lpPropTagArray->cValues * sizeof(SPropValue));

    // Prepare the query for the PT_UNSPECIFIED case
    sprintf_s(buff, 512, "SELECT * FROM %s WHERE %s = ? \
                                AND TRANSACT_ID IN ( 0, ? ) \
                                ORDER BY TRANSACT_ID DESC \
                                LIMIT 1",
		            tableName, OTLKCON_PT_UNSPECIFIED_PROPTAG_STR );
    sqlRes = sqlite3_prepare( sqlObj->databaseHandle, buff,
                -1, &queryStmt_unspecified, 0);
    if(SQLITE_OK != sqlRes)
    {
        OTLKCON_LOG_PRINTF(OTLKCON_LOG_LEVEL_INFO, OTLKCON_LOG_TARGET_ALL, 
            L"%S sqlite3_prepare error",
            sqlite3_errmsg(sqlObj->databaseHandle));
    }

    // Prepare the query for the regular case
    sprintf_s(buff, 512, "SELECT * FROM %s WHERE %s = ? \
                                AND TRANSACT_ID IN ( 0, %d ) \
                                ORDER BY TRANSACT_ID DESC \
                                LIMIT 1",
		            tableName, OTLKCON_REGULAR_PROPTAG_STR, objNum );
    sqlRes = sqlite3_prepare( sqlObj->databaseHandle, buff,
                -1, &queryStmt_regular, 0);
    if(SQLITE_OK != sqlRes)
    {
        OTLKCON_LOG_PRINTF(OTLKCON_LOG_LEVEL_INFO, OTLKCON_LOG_TARGET_ALL, 
            L"%S sqlite3_prepare error",
            sqlite3_errmsg(sqlObj->databaseHandle));
    }

    // Now loop the values.
    for(ULONG i=0; i<lpPropTagArray->cValues; i++)
    {    
        currTag = lpPropTagArray->aulPropTag[i];

        //char * propName = prop2str->get(currTag);
        //if(propName)
        //{
        //    OTLKCON_LOG_PRINTF(OTLKCON_LOG_LEVEL_INFO,  OTLKCON_LOG_TARGET_ALL, 
        //        L"<%S> %S->O_IProp::GetProps() : Asking for 0x%0.8X ( %S )\n", 
        //            objName, className, currTag, propName);
        //    memObj->lpFreeBuffer(propName);
        //}
        //else
        //    OTLKCON_LOG_PRINTF(OTLKCON_LOG_LEVEL_INFO,  OTLKCON_LOG_TARGET_ALL, 
        //        L"<%S> %S->O_IProp::GetProps() : Asking for 0x%0.8X\n", 
        //        objName, className, currTag);
        
        if( PROP_TYPE(currTag) == PT_UNSPECIFIED )
        {
            //OTLKCON_DEBUGBREAK;
            // Strip the property type by ANDing with 11111111111111110000000000000000
            queryStmt = queryStmt_unspecified;
        }
        else
        {
            queryStmt = queryStmt_regular;
        }

        // Check that the property is not in the 'deletedProperties'
        // set.
        found = -1;
        if( isTransacted )
        {
            // Properties are deleted by IDs not by full Tags
            if( deletedProperties.find( PROP_ID(lpPropTagArray->aulPropTag[i]) )
                    != deletedProperties.end() )
            {
                // Property has been deleted
                found = 0;
            }
        }

        // Do we still not know about it?
        if( found == -1 )
        {
            // Run the query
            sqlRes = sqlite3_bind_int(queryStmt, 1,
                            currTag);
            sqlRes = sqlite3_step(queryStmt);
            switch( sqlRes )
            {
                case SQLITE_DONE:
                    // No rows found.
                    found = 0;
                    break;

                case SQLITE_ROW:
                    // Rows found.
                    found = 1;
                    break;

                default:
                    // This was not expected
                    // FIXME:  This is really an error.  Set PT_ERROR
                    found = 0;
                    OTLKCON_DEBUGBREAK;
            }
        }

        if( found == 0 )
        {
            //PropTag not found
            if( computedProperties.find( lpPropTagArray->aulPropTag[i] )
                                              != computedProperties.end() )
            {
                (*lppPropArray)[i].ulPropTag = lpPropTagArray->aulPropTag[i];
                (*lppPropArray)[i].Value.err = MAPI_E_NOT_FOUND;
            }
            else
            {
                errFlag =1;
                (*lppPropArray)[i].ulPropTag 
                    = PROP_TAG(PT_ERROR, PROP_ID(lpPropTagArray->aulPropTag[i]));
                (*lppPropArray)[i].Value.err = MAPI_E_NOT_FOUND;
            }
                        
            sqlRes = sqlite3_reset(queryStmt);
            if ( sqlRes != SQLITE_OK )
            {
                OTLKCON_LOG_PRINTF(OTLKCON_LOG_LEVEL_INFO, OTLKCON_LOG_TARGET_ALL, 
                    L"IProp::GetProps sqlite3_reset error: %S", 
                    sqlite3_errmsg(sqlObj->databaseHandle));
                SetLastError(E_FAIL, "Error accessing database");
                result = E_FAIL;
                goto intgetprops_failed;
            }
            continue;
        }
        
        OTLKCON_LOG_PRINTF(OTLKCON_LOG_LEVEL_INFO,  OTLKCON_LOG_TARGET_ALL, L"%S->O_IProp::GetProps() : Found 0x%0.8X\n", className, currTag);

        //(*lppPropArray)[i].ulPropTag = lpPropTagArray->aulPropTag[i];
        (*lppPropArray)[i].ulPropTag =  sqlite3_column_int(queryStmt, OTLKCON_IPROP_TABLE_PROPTAG_COL);

        //Parse the data
		ULONG ulPropTag = lpPropTagArray->aulPropTag[i];
		if(PT_UNSPECIFIED == PROP_TYPE(ulPropTag))
		{
			ulPropTag = (*lppPropArray)[i].ulPropTag;
		} // if

	    switch( PROP_TYPE(ulPropTag) )
	    {
	    case PT_I2:
		    // short i 
		    (*lppPropArray)[i].Value.i
			    = sqlite3_column_int(queryStmt, OTLKCON_IPROP_TABLE_DATA_COL);
		    break;

	    case PT_LONG:
		    // long l or ul??
		    (*lppPropArray)[i].Value.l 
			    = sqlite3_column_int(queryStmt, OTLKCON_IPROP_TABLE_DATA_COL);
		    break;

	    case PT_BOOLEAN:
		    // long l or ul??
		    (*lppPropArray)[i].Value.b 
			    = sqlite3_column_int(queryStmt, OTLKCON_IPROP_TABLE_DATA_COL);
		    break;

	    case PT_STRING8:
		    // LPSTR lpszA 
		    // How do you free this memory??
		    data = (unsigned char *)sqlite3_column_text(queryStmt, 
			    OTLKCON_IPROP_TABLE_DATA_COL);
		    (*lppPropArray)[i].Value.lpszA = memObj->_strdupMore((*lppPropArray), (const char *)data );
		    break;

	    case PT_UNICODE:
		    // LPSTR lpszW
		    // How do you free this memory??
		    data = (unsigned char *)sqlite3_column_text16(queryStmt, 
			    OTLKCON_IPROP_TABLE_DATA_COL);
		    (*lppPropArray)[i].Value.lpszW = memObj->w_strdupMore(&((*lppPropArray)[i]), (const wchar_t *)data );
		    break;

	    case PT_BINARY:
		    // SBinary bin
		    tmpBinSize = sqlite3_column_bytes(queryStmt, 
			    OTLKCON_IPROP_TABLE_DATA_COL);
		    memObj->lpAllocateMore(tmpBinSize+1, (*lppPropArray), (LPVOID*)&tmpBin);
		    memset(tmpBin, 0, tmpBinSize+1);
		    tmpSQLBin = (LPBYTE)sqlite3_column_blob(queryStmt, 
			    OTLKCON_IPROP_TABLE_DATA_COL);
		    memcpy(tmpBin, tmpSQLBin, tmpBinSize);
		    (*lppPropArray)[i].Value.bin.lpb = (LPBYTE)tmpBin;
		    (*lppPropArray)[i].Value.bin.cb  = tmpBinSize;
		    break;

	    case PT_CLSID:
		    // LPGUID lpguid
		    tmpBinSize = sqlite3_column_bytes(queryStmt, 
			    OTLKCON_IPROP_TABLE_DATA_COL);
		    if( tmpBinSize != 16 )
		    {
			    OTLKCON_DEBUGBREAK;
			    // FIXME: Set PT_ERROR
		    }

		    memObj->lpAllocateMore(tmpBinSize+1, (*lppPropArray), (LPVOID*)&tmpBin);
		    memset(tmpBin, 0, tmpBinSize+1);
		    tmpSQLBin = (LPBYTE)sqlite3_column_blob(queryStmt, 
			    OTLKCON_IPROP_TABLE_DATA_COL);
		    memcpy(tmpBin, tmpSQLBin, tmpBinSize);
		    (*lppPropArray)[i].Value.lpguid = (LPGUID)tmpBin;
		    break;

	    case PT_OBJECT:
		    // According to my interpretion of the docs we 
		    // shouldn't do this since PT_OBJECTs need to be
		    // accessed by OpenProperty(), but somoe clients
		    // use HrGetProps() to open table properties
		    // anyway.  Hence, take care of that.

		    (*lppPropArray)[i].Value.x = PR_NULL;

		    /*// Open properties we are familiar with.
		    switch( lpPropTagArray->aulPropTag[i] )
		    {
		    case PR_CONTAINER_HIERARCHY:
		    case PR_CONTAINER_CONTENTS:
		    result = OpenProperty(lpPropTagArray->aulPropTag[i], 
		    &IID_IMAPITable, 0, MAPI_MODIFY, 
		    (LPUNKNOWN *)&tmpBin);
		    if( result != S_OK )
		    {
		    // OpenProperty() failed
		    OTLKCON_DEBUGBREAK;
		    }
		    // FIXME: 64-bit pointer truncated warning.
		    // MAPI needs to be updated; Forces pointers
		    // to be stored in a 'LONG' in SPropValue.Value
		    #pragma warning ( disable: 4311 )
		    (*lppPropArray)[i].Value.x = (LONG)tmpBin;
		    #pragma warning ( default: 4311 )
		    t = (O_ITable*)tmpBin;
		    t->Dump(0);
		    t->GetRowCount(0, (ULONG*)&rowCount);
		    break;

		    default:
		    // A PT_OBJECT we do not know how to open
		    OTLKCON_LOG_PRINTF(OTLKCON_LOG_LEVEL_INFO, OTLKCON_LOG_TARGET_ALL, 
		    L"IProp::GetProps unknown PT_OBJECT: 0x%0.8X", 
		    lpPropTagArray->aulPropTag[i] );
		    OTLKCON_DEBUGBREAK;
		    break;
		    }*/
		    break;

	    case PT_SYSTIME:
		    {
			    FILETIME tempFT = {0};
			    tmpBinSize = sqlite3_column_bytes(queryStmt, 
				    OTLKCON_IPROP_TABLE_DATA_COL);
			    if( tmpBinSize != sizeof(FILETIME) )
			    {
                    // FIXME: Set PT_ERROR
				    OTLKCON_DEBUGBREAK;
			    }

			    tmpSQLBin = (LPBYTE)sqlite3_column_blob(queryStmt, 
				    OTLKCON_IPROP_TABLE_DATA_COL);
			    memcpy(&tempFT, tmpSQLBin, sizeof(FILETIME));
			    (*lppPropArray)[i].Value.ft = tempFT;
		    }
		    break;

	    default:
		    OTLKCON_LOG_PRINTF(OTLKCON_LOG_LEVEL_INFO, OTLKCON_LOG_TARGET_ALL, 
			    L"O_IProp::_IntGetProps() unknown type: 0x%0.8X",
			    PROP_TYPE((*lppPropArray)[i].ulPropTag) );
		    OTLKCON_DEBUGBREAK
		    // FIXME: Set PT_ERROR;
	    }
                   
        sqlRes = sqlite3_reset(queryStmt);
        if ( sqlRes != SQLITE_OK )
        {
            OTLKCON_LOG_PRINTF(OTLKCON_LOG_LEVEL_INFO, OTLKCON_LOG_TARGET_ALL, 
                L"IProp::GetProps sqlite3_reset error: %S", 
                sqlite3_errmsg(sqlObj->databaseHandle));
            SetLastError(E_FAIL, "Error accessing database");
            result = E_FAIL;
            goto intgetprops_failed;
        }
    }

    /*char *strBuff;
    int strBuffSize = OTLKCON_LOG_MSG_MAX_LEN-1;
    memObj->lpAllocateBuffer(OTLKCON_LOG_MSG_MAX_LEN-1, (LPVOID *)&strBuff);
    memset(strBuff, 0, OTLKCON_LOG_MSG_MAX_LEN-1);
    result = otlkcon_obj2xml_begin(&strBuff, OTLKCON_LOG_MSG_MAX_LEN-1);
    for(int j=0;j<*lpcValues;j++)
    {
        sprintf(strBuff+strlen(strBuff), "<object>\n");
        result = otlkcon_propValue2xml(*lppPropArray+j, OTLKCON_OBJ2XML_SHOW_BINARY, 0,
                                memObj->lpAllocateBuffer, memObj->lpAllocateMore, memObj->lpFreeBuffer, 
                                &strBuff, strBuffSize);
        sprintf(strBuff+strlen(strBuff), "</object>\n");
    }
    result = otlkcon_obj2xml_end(&strBuff, OTLKCON_LOG_MSG_MAX_LEN-1);
    OTLKCON_LOG_PRINTF(OTLKCON_LOG_LEVEL_INFO, OTLKCON_LOG_TARGET_SM|OTLKCON_LOG_FLAG_NO_DATE, L"%S", strBuff);
    memObj->lpFreeBuffer(strBuff);*/
//getprops_end:
    
    result = sqlObj->sqlCmdCommit();
	if(FAILED(result))
	{
		OTLKCON_DEBUGBREAK;
	}

    OTLKCON_LEAVECRITICALSECTION(&objectCS, "OBJ> IProp::GetProps");
    OTLKCON_LEAVECRITICALSECTION(&otlkcon_sqlite_cs, "SQL> O_IProp::GetProps");

    if( errFlag )
        result = MAPI_W_ERRORS_RETURNED;

    return result;

intgetprops_failed:

    result = sqlObj->sqlCmdCommit();
	if(FAILED(result))
	{
		OTLKCON_DEBUGBREAK;
	}

    OTLKCON_LEAVECRITICALSECTION(&objectCS, "OBJ> IProp::GetProps");
    OTLKCON_LEAVECRITICALSECTION(&otlkcon_sqlite_cs, "SQL> O_IProp::GetProps");

    return result;
}

STDMETHODIMP O_IProp::GetPropList( ULONG ulFlags,
                                  LPSPropTagArray *lppPropTagArray)
{
    HRESULT result = S_OK;
    char *queryStr;
    int sqlRes = 0, rowCount = 0;
    const char **queryRemainder = NULL;
    sqlite3_stmt * queryStmt = NULL;
    vector<ULONG> tags;
    ULONG currTag;

//    Validate_IMAPIProp_GetPropList( this, ulFlags, lppPropTagArray );
    OTLKCON_LOG_PRINTF(OTLKCON_LOG_LEVEL_INFO,  OTLKCON_LOG_TARGET_ALL, 
        L"inside O_IProp::GetPropList()\n" );

    lastCalledFunctionID = OTLKCON_IPROP_GETPROPLIST;
    strcpy_s(lastCalledFunctionName, OTLKCON_GETLASTERROR_FUNCTION_NAME_SIZE+1,
        OTLKCON_IPROP_GETPROPLIST_STR);
    result = SetLastError(S_OK, "");

    OTLKCON_ENTERCRITICALSECTION(&otlkcon_sqlite_cs, "SQL> O_IProp::GetPropList");
    OTLKCON_ENTERCRITICALSECTION(&objectCS, "OBJ> IProp::GetPropList");

    if( ulFlags & MAPI_UNICODE )
    {
        result = MAPI_E_BAD_CHARWIDTH;
        goto getproplist_end;
    }

    otlkcon_sqlite_helper * sqlObj = GetSqlObj();
	if(NULL == sqlObj)
	{
		result = E_FAIL;
        goto getproplist_end;
	} // if
    // Count all the properties
    sqlRes = sqlObj->sqlCmdSingleInt( &rowCount, 0,
                    "SELECT COUNT(*) FROM ( SELECT DISTINCT PropTag FROM %s )", 
                    tableName );

    // Add computed properties
    rowCount += (int)computedProperties.size();

    //Get all properties
    sqlRes = sqlObj->buildQuery( &queryStr,
                        "SELECT DISTINCT PropTag FROM %s", tableName );

    tags.clear();
    sqlite3_prepare( sqlObj->databaseHandle, queryStr, -1, 
                                &queryStmt, queryRemainder);
    while( (sqlRes = sqlite3_step(queryStmt)) == SQLITE_ROW )
    {
        currTag = sqlite3_column_int(queryStmt,0);

        // Check that the property is not in
        // the 'deletedProperties' set
        if( !isTransacted || ( deletedProperties.find( PROP_ID(currTag) )
                                              == deletedProperties.end() ) )
        {
            tags.push_back( currTag );
        }
        else
        {
            rowCount--;
        }
    }
    sqlRes = sqlite3_finalize(queryStmt);
    if ( sqlRes != SQLITE_OK )
    {
        OTLKCON_LOG_PRINTF(OTLKCON_LOG_LEVEL_INFO, OTLKCON_LOG_TARGET_ALL, 
            L"IProp::GetPropList sqlite3_finalize error: %S", 
            sqlite3_errmsg(sqlObj->databaseHandle));
        result = E_FAIL;
        goto getproplist_end;
    }

    // build the PropTagArray
    *lppPropTagArray 
        = (LPSPropTagArray)memObj->malloc( CbNewSPropTagArray(rowCount) );

    ULONG i;
    for(i = 0; i<tags.size(); i++ )
    {
        (*lppPropTagArray)->aulPropTag[i] = tags[i];
    }

    // 'i' will be used again as an iterator below
    i--;

    // Add computed properties to the list
    if( entryID->typeID == MAPI_STORE )
    {
        // for message stores, PR_ENTRYID is both
        vector<ULONG>::iterator propRes 
            = find(tags.begin(), tags.end(), PR_ENTRYID);
        if( propRes == tags.end() )
        {
            // Possible, but signals issues.
            // Asking for props before PR_ENTRYID is set??
            OTLKCON_DEBUGBREAK;
            (*lppPropTagArray)->aulPropTag[i++] = PR_ENTRYID;
        }
    }
    
    for(set <ULONG>::iterator j = computedProperties.begin(); 
                                    j!=computedProperties.end(); j++)
    {
        (*lppPropTagArray)->aulPropTag[i++] = *j;
    }

    (*lppPropTagArray)->cValues = i;
    memObj->free(queryStr);

getproplist_end:
    OTLKCON_LEAVECRITICALSECTION(&objectCS, "OBJ> IProp::GetPropList");
    OTLKCON_LEAVECRITICALSECTION(&otlkcon_sqlite_cs, "SQL> O_IProp::GetPropList");

    return result;
}

STDMETHODIMP O_IProp::SetProps(ULONG cValues, 
                               LPSPropValue lpPropArray,
                               LPSPropProblemArray *lppProblems )
{
    HRESULT result = S_OK;
    SPropValue currPV = {0};
    int problemIndex = 0;

    //Validate_IMAPIProp_SetProps( this, cValues, lpPropArray, lppProblems );
    lastCalledFunctionID = OTLKCON_IPROP_SETPROPS;
    strcpy_s(lastCalledFunctionName, OTLKCON_GETLASTERROR_FUNCTION_NAME_SIZE+1,
        OTLKCON_IPROP_SETPROPS_STR);
    result = SetLastError(S_OK, "");
    
    memset(&currPV, 0, sizeof(currPV));

    if( cValues == 0 )
    {
        //TODO: Error condition
        OTLKCON_DEBUGBREAK;
    }

    if( lppProblems )
    {
        // TODO: Should be free'ed on E_FAIL
        // FIXME: This array should be sized to the exact number of errors
        *lppProblems 
            = (LPSPropProblemArray)memObj->malloc( CbNewSPropProblemArray(cValues) );
    }

    for( ULONG i=0; i<cValues; i++ )
    {
        currPV=lpPropArray[i];
    
        //Check for exceptions
        switch( currPV.ulPropTag )
        {
            case PR_STORE_SUPPORT_MASK:
                //Set MAPI_E_COMPUTED in ProblemArray
                if( lppProblems )
                {
                    (*lppProblems)->aProblem[i].ulIndex   = i;
                    (*lppProblems)->aProblem[i].scode     = MAPI_E_COMPUTED;
                    (*lppProblems)->aProblem[i].ulPropTag = currPV.ulPropTag;
                }
                continue;

            default:
                break;
        }

        //Check for types
        switch( PROP_TYPE( currPV.ulPropTag ) )
        {
            case PT_ERROR:
                //Report No errors but ignore
                //TODO: Need a way to communicate this
                //      to _IntSetProps()
                if( lppProblems )
                {
                    (*lppProblems)->aProblem[problemIndex].ulIndex   = i;
                    (*lppProblems)->aProblem[problemIndex].scode     = MAPI_E_INVALID_TYPE;
                    (*lppProblems)->aProblem[problemIndex++].ulPropTag = currPV.ulPropTag;
                }
                continue;

            case  PT_OBJECT:
                //Set MAPI_E_INVALID_TYPE in ProblemArray
                if( lppProblems )
                {
                    (*lppProblems)->aProblem[problemIndex].ulIndex   = i;
                    (*lppProblems)->aProblem[problemIndex].scode     = MAPI_E_INVALID_TYPE;
                    (*lppProblems)->aProblem[problemIndex++].ulPropTag = currPV.ulPropTag;
                }
                continue;

            default:
                break;
        }
    }

    if( lppProblems )
        (*lppProblems)->cProblem = problemIndex;

    result = _IntSetProps( cValues,lpPropArray,lppProblems );
	
	//Henry 30.11.2005
	//we call that to notify clients
	if(SUCCEEDED(result) && !isTransacted)
	{
		result = this->SaveChanges(KEEP_OPEN_READWRITE);
	} // if

    return result;
}

STDMETHODIMP O_IProp::_BuildPropQuery(
	otlkcon_sqlite_helper *	sqlObj,
	const char *			lpszTableName,
	LPSPropValue			lpPropValue,
	int						transactID,
	BOOL					isReference,
	char **			lppQueryString)
{
	HRESULT hResult = E_INVALIDARG;
	if(NULL != lpszTableName && NULL != lpPropValue && NULL != lppQueryString)
	{
		*lppQueryString = NULL;
		char * queryStr = NULL;
		int rowSize = 0;
		hResult = S_OK;
		HRESULT sqlRes = 0;
		switch( PROP_TYPE(lpPropValue->ulPropTag) )
		{
		case PT_I2:
			/* short i */
			rowSize = 5*sizeof(ULONG)+sizeof(short);
			sqlRes = sqlObj->buildQuery( &queryStr,
				"INSERT OR REPLACE INTO %s VALUES ( %d, %d, %d, %d, %d, '%d' );",
				lpszTableName, rowSize, lpPropValue->ulPropTag, 0, transactID, isReference, lpPropValue->Value.i );
			break;

		case PT_LONG:
			/* long l or ul??*/
			rowSize = 5*sizeof(ULONG)+sizeof(long);
			sqlRes = sqlObj->buildQuery( &queryStr,
				"INSERT OR REPLACE INTO %s VALUES ( %d, %d, %d, %d, %d, '%ld' );",
				lpszTableName, rowSize, lpPropValue->ulPropTag, 0, transactID, isReference, lpPropValue->Value.l );
			break;

		case PT_R4:
			/* float flt */
			rowSize = 5*sizeof(ULONG)+sizeof(float);
			sqlRes = sqlObj->buildQuery( &queryStr,
				"INSERT OR REPLACE INTO %s VALUES ( %d, %d, %d, %d, %d, '%g' );",
				lpszTableName, rowSize, lpPropValue->ulPropTag, 0, transactID, isReference, lpPropValue->Value.flt );
			break;

		case PT_R8:
			/* double dbl */
			rowSize = 5*sizeof(ULONG)+sizeof(double);
			sqlRes = sqlObj->buildQuery( &queryStr,
				"INSERT OR REPLACE INTO %s VALUES ( %d, %d, %d, %d, %d,'%g' );",
				lpszTableName, rowSize, lpPropValue->ulPropTag, 0, transactID, isReference, lpPropValue->Value.dbl );
			break;

		case PT_BOOLEAN:
			/* unsigned short int b*/
			rowSize = 5*sizeof(ULONG)+sizeof(unsigned short);
			sqlRes = sqlObj->buildQuery( &queryStr,
				"INSERT OR REPLACE INTO %s VALUES ( %d, %d, %d, %d, %d,'%d' );",
				lpszTableName, rowSize, lpPropValue->ulPropTag, 0, transactID, isReference, lpPropValue->Value.b );
			break;

		case PT_CURRENCY:
			/* CURRENCY cur */
			/* ??? not sure about this ??? */
			rowSize = 5*sizeof(ULONG)+sizeof(CURRENCY);
			sqlRes = sqlObj->buildQuery( &queryStr,
				"INSERT OR REPLACE INTO %s VALUES ( %d, %d, %d, %d, %d, :DAT );",
				lpszTableName, rowSize, lpPropValue->ulPropTag, 0, transactID, isReference );
			break;

		case PT_APPTIME:
			/* double at */
			rowSize = 5*sizeof(ULONG)+sizeof(double);
			sqlRes = sqlObj->buildQuery( &queryStr,
				"INSERT OR REPLACE INTO %s VALUES ( %d, %d, %d, %d, %d,'%g' );",
				lpszTableName, rowSize, lpPropValue->ulPropTag, 0, transactID, isReference, lpPropValue->Value.at );
			break;

		case PT_SYSTIME:
			/* FILETIME ft */
			rowSize = 5*sizeof(ULONG)+sizeof(FILETIME);
			sqlRes = sqlObj->buildQuery( &queryStr,
				"INSERT OR REPLACE INTO %s VALUES ( %d, %d, %d, %d, %d, :DAT );",
				lpszTableName, rowSize, lpPropValue->ulPropTag, 0, transactID, isReference );
			break;

		case PT_STRING8:
			/* LPSTR lpszA */
			rowSize = 5*sizeof(ULONG)+(ULONG)strlen(lpPropValue->Value.lpszA);
			sqlRes = sqlObj->buildQuery( &queryStr,
				"INSERT OR REPLACE INTO %s VALUES ( %d, %d, %d, %d, %d, '%s' );",
				lpszTableName, rowSize, lpPropValue->ulPropTag, 0, transactID, isReference, lpPropValue->Value.lpszA );
			break;

		case PT_BINARY:
			/* SBinary bin */
			rowSize = 5*sizeof(ULONG)+lpPropValue->Value.bin.cb;
			sqlRes = sqlObj->buildQuery( &queryStr,
				"INSERT OR REPLACE INTO %s VALUES (%d, %d, %d, %d, %d, :DAT );",
				lpszTableName, rowSize, lpPropValue->ulPropTag, 0, transactID, isReference );
			break;

		case PT_UNICODE:
			/* LPWSTR lpszW */
			rowSize = 5*sizeof(ULONG)+(ULONG)wcslen(lpPropValue->Value.lpszW);
			sqlRes = sqlObj->buildQuery( &queryStr,
				"INSERT OR REPLACE INTO %s VALUES ( %d, %d, %d, %d, %d, '%S' );",
				lpszTableName, rowSize, lpPropValue->ulPropTag, 0, transactID, isReference, lpPropValue->Value.lpszW );
			break;

		case PT_CLSID:
			// LPGUID lpguid 
			rowSize = 5*sizeof(ULONG)+sizeof(struct _GUID);
			sqlRes = sqlObj->buildQuery( &queryStr,
				"INSERT OR REPLACE INTO %s VALUES ( %d, %d, %d, %d, %d, :DAT );",
				lpszTableName, rowSize, lpPropValue->ulPropTag, 0, transactID, isReference );
			break;

		case PT_I8:
			// LARGE_INTEGER li
			rowSize = 5*sizeof(ULONG)+sizeof(LARGE_INTEGER);
			sqlRes = sqlObj->buildQuery( &queryStr,
				"INSERT OR REPLACE INTO %s VALUES ( %d, %d, %d, %d, %d, '%d' );",
				lpszTableName, rowSize, lpPropValue->ulPropTag, 0, transactID, isReference, lpPropValue->Value.li );
			break;

			// TODO: Fix multi-valued properties and transacted MV properties
/*
		case PT_MV_I2:
			// SShortArray MVi
			isMV=1;
			for(j=0; j<lpPropArray[i].Value.MVi.cValues; j++)
			{
				sqlRes = sqlObj->buildQuery( &queryStr,
					"INSERT OR REPLACE INTO %s VALUES ( %d, %d, %d, %d, '%d' );",
					tableName, currPV.ulPropTag, j, 0, 0, currPV.Value.MVi.lpi[j]);
				sqlRes = sqlite3_exec(sqlObj->databaseHandle, queryStr, NULL, 
					NULL, &errMsg);
			}
			break;

		case PT_MV_LONG:
			isMV=1;
			// SLongArray MVl
			for(j=0; j<lpPropArray[i].Value.MVl.cValues; j++)
			{
				sqlRes = sqlObj->buildQuery( &queryStr,
					"INSERT OR REPLACE INTO %s VALUES ( %d, %d, %d, %d, '%ld' );",
					tableName, currPV.ulPropTag, j, 0, 0, currPV.Value.MVl.lpl[j]);
				sqlRes = sqlite3_exec(sqlObj->databaseHandle, queryStr, NULL, 
					NULL, &errMsg);
			}
			break;

		case PT_MV_R4:
			isMV=1;
			// SRealArray MVflt 
			for(j=0; j<lpPropArray[i].Value.MVflt.cValues; j++)
			{
				sqlRes = sqlObj->buildQuery( &queryStr,
					"INSERT OR REPLACE INTO %s VALUES ( %d, %d, %d, %d, '%g' );",
					tableName, currPV.ulPropTag, j, 0, 0, currPV.Value.MVflt.lpflt);
				sqlRes = sqlite3_exec(sqlObj->databaseHandle, queryStr, NULL, 
					NULL, &errMsg);
			}
			break;

		case PT_MV_DOUBLE:
			isMV=1;
			// SDoubleArray MVdbl 
			for(j=0; j<lpPropArray[i].Value.MVdbl.cValues; j++)
			{
				sqlRes = sqlObj->buildQuery( &queryStr,
					"INSERT OR REPLACE INTO %s VALUES ( %d, %d, %d, %d, '%g' );",
					tableName, currPV.ulPropTag, j, 0, 0, currPV.Value.MVdbl.lpdbl);
				sqlRes = sqlite3_exec(sqlObj->databaseHandle, queryStr, NULL, 
					NULL, &errMsg);
			}
			break;

		case PT_MV_CURRENCY:
			isMV=1;
			// SCurrencyArray MVcur
			;
			break;

		case PT_MV_APPTIME:
			isMV=1;
			// SAppTimeArray MVat
			for(j=0; j<lpPropArray[i].Value.MVat.cValues; j++)
			{
				;
			}
			break;

		case PT_MV_SYSTIME:
			// SDateTimeArray MVft 
			for(j=0; j<lpPropArray[i].Value.MVat.cValues; j++)
			{
				;
			}
			break;

		case PT_MV_BINARY:
			// SBinaryArray MVbin 
			for(j=0; j<lpPropArray[i].Value.MVbin.cValues; j++)
			{
				;
			}
			break;

		case PT_MV_STRING8:
			// SLPSTRArray MVszA 
			for(j=0; j<lpPropArray[i].Value.MVszA.cValues; j++)
			{
				;
			}
			break;

		case PT_MV_I8:
			// SLargeIntegerArray MVli 
			for(j=0; j<lpPropArray[i].Value.MVli.cValues; j++)
			{
				;
			}
			break;

*/
		case PT_ERROR:
		case PT_NULL:
			OTLKCON_LOG_PRINTF(OTLKCON_LOG_LEVEL_INFO, OTLKCON_LOG_TARGET_ALL, 
				L"IProp::SetProps Setting a PT_ERROR or PT_NULL");
			//we do nothing
			break;

		case PT_OBJECT:
			// This is PT_OBJECT only for internal use
			// NB: the value is not saved!!!
			// TODO:  Actually, what should happen is that the DATA column
			//        should hold the name of the table that represents this
			//        object.
			sqlRes = sqlObj->buildQuery( &queryStr,
				"INSERT OR REPLACE INTO %s ( PropTag, MV_SEQ, TRANSACT_ID, IS_REF ) VALUES ( %d, %d, %d, %d );",
				lpszTableName, lpPropValue->ulPropTag, 0, transactID, 1/*IsReference*/ );
			break;

		default:
			OTLKCON_LOG_PRINTF(OTLKCON_LOG_LEVEL_INFO, OTLKCON_LOG_TARGET_ALL, 
				L"%d : unknown property type.\n", lpPropValue->ulPropTag);
			hResult = MAPI_E_NOT_SUPPORTED;
			break;
		} //switch
		if(NULL != queryStr)
		{
			*lppQueryString = queryStr;
		} // if
	} // if valid arguments
	return hResult;
}

STDMETHODIMP O_IProp::_BindValue(sqlite3_stmt *queryStmt, LPSPropValue lpPropValue)
{
	HRESULT hResult = E_INVALIDARG;
	if(NULL != queryStmt && NULL != lpPropValue)
	{
		HRESULT sqlRes = 0;
		switch( PROP_TYPE(PROP_TYPE(lpPropValue->ulPropTag)) )
		{
		case PT_CURRENCY:
			sqlRes = sqlite3_bind_blob(queryStmt, 1, &(lpPropValue->Value.cur),
				sizeof(lpPropValue->Value.cur), SQLITE_TRANSIENT);
			break;

		case PT_BINARY:
			sqlRes = sqlite3_bind_blob(queryStmt, 1, lpPropValue->Value.bin.lpb,
				lpPropValue->Value.bin.cb, SQLITE_TRANSIENT);
			break;

		case PT_CLSID:
			sqlRes = sqlite3_bind_blob(queryStmt, 1, lpPropValue->Value.lpguid,
				sizeof(struct _GUID), SQLITE_TRANSIENT);
			break;

		case PT_SYSTIME:
			sqlRes = sqlite3_bind_blob(queryStmt, 1, &(lpPropValue->Value.ft),
				sizeof(lpPropValue->Value.ft), SQLITE_TRANSIENT);
			break;
		}
		hResult = SQLITE_OK == sqlRes?S_OK:E_FAIL;
	} // if
	return hResult;
}

// Bypasses transactions and sychronization.  Was useful for getting
// around those issues.
STDMETHODIMP O_IProp::_StoreOneProp(LPSPropValue lpPropValue)
{
	HRESULT hResult = E_INVALIDARG;
	if(NULL != lpPropValue)
	{
		hResult = S_OK;
		otlkcon_sqlite_helper * sqlObj = GetSqlObj();
		if(NULL != sqlObj)
		{
			char * queryStr = NULL;
			hResult = _BuildPropQuery(
				sqlObj,
				diskTableName,
				lpPropValue,
				0,
				FALSE,
				&queryStr);
			if(SUCCEEDED(hResult) && NULL != queryStr)
			{
				sqlite3_stmt * queryStmt = NULL;
				const char *queryRemainder = NULL;
				
				HRESULT sqlRes = sqlite3_prepare( sqlObj->databaseHandle, queryStr, -1, 
					&queryStmt, &queryRemainder);
				if ( sqlRes != SQLITE_OK )
				{
					OTLKCON_LOG_PRINTF(OTLKCON_LOG_LEVEL_INFO, OTLKCON_LOG_TARGET_ALL, 
						L"IProp::_IntSetProps sqlite3_prepare error: %S",
						sqlite3_errmsg(sqlObj->databaseHandle));
					hResult = E_FAIL;
					// TODO
					// Henry: we need to set problem for that property 
					// and continue setting all other properties
					//				goto setprops_fail;
				}
				if(SQLITE_OK == sqlRes) 
				{
					if(SUCCEEDED(_BindValue(queryStmt, lpPropValue)))
					{
						sqlite3_step(queryStmt);
					} // if

					sqlRes = sqlite3_finalize(queryStmt);
					if ( sqlRes != SQLITE_OK )
					{
						OTLKCON_LOG_PRINTF(OTLKCON_LOG_LEVEL_INFO, OTLKCON_LOG_TARGET_ALL, 
							L"IProp::_IntSetProps sqlite3_finalize error: %S", 
							sqlite3_errmsg(sqlObj->databaseHandle));
						hResult = E_FAIL;
					}
				} // if
				memObj->free(queryStr);
			} // if
		} // if
	} // if
	return hResult;
}


// Skips exceptions check.  
// Any property can be modified.
// lppProblems needs to be NULL if called directly
STDMETHODIMP O_IProp::_IntSetProps(ULONG cValues, 
                               LPSPropValue lpPropArray,
                               LPSPropProblemArray *lppProblems )
{
    HRESULT result = S_OK;
    int sqlRes     = 0;
    sqlite3_stmt * queryStmt = NULL;
    char * queryStr = NULL;
    char * errMsg = NULL;
    const char *queryRemainder = NULL;
    SPropValue currPV = {0};
    int isMV=0, rowSize=0;
    //int rowSize;
    ULONG skip=0,j=0;
	char buff[512] ={0};

    lastCalledFunctionID = OTLKCON_IPROP_SETPROPS;
    strcpy_s(lastCalledFunctionName, OTLKCON_GETLASTERROR_FUNCTION_NAME_SIZE+1,
        OTLKCON_IPROP_SETPROPS_STR);
    result = SetLastError(S_OK, "");

    if( cValues == 0 )
    {
        //TODO: Error condition
        OTLKCON_DEBUGBREAK;
        result = SetLastError(E_FAIL, "Invalid argument.");
//        result = E_FAIL;
		return E_FAIL;
//        goto setprops_failed;
    }  

	OTLKCON_ENTERCRITICALSECTION(&otlkcon_sqlite_cs, "SQL> O_IProp::SetProps");
    OTLKCON_ENTERCRITICALSECTION(&objectCS, "OBJ> IProp::SetProps");

	otlkcon_sqlite_helper * sqlObj = GetSqlObj();
	if(NULL == sqlObj)
	{
		result = E_FAIL;
        goto setprops_fail;
	} // if
    
	// TODO: We should check for table version overflow
    if( isTransacted && tblVersion == 0 )
    {
        sqlRes = sqlObj->sqlCmdSingleInt( &tblVersion, 0,
            "SELECT version FROM object_version_table \
                WHERE name = '%s'", diskTableName);

        if ( sqlRes != S_OK )
        {
            OTLKCON_DEBUGBREAK;
            result = E_FAIL;
            goto setprops_fail;
        }
    }

//    //Refresh the database handle if needed
//    result = GetSqlObj();
//
//    
	//Begin the transaction
	//20.11.2005 Henry: it is better to insert all properties in one transaction
	//it is much faster

	sqlRes = sqlObj->sqlCmdBegin();
	if(FAILED(sqlRes))
	{
		result = E_FAIL;
        goto setprops_fail;
	} // if

	sprintf_s(buff, 512, "INSERT OR REPLACE INTO %s VALUES ( ?, ?, ?, ?, ?, ? )",
		tableName );

    sqlRes = sqlite3_prepare( sqlObj->databaseHandle, buff,
                -1, &queryStmt, 0);
    if(SQLITE_OK != sqlRes)
    {
        OTLKCON_LOG_PRINTF(OTLKCON_LOG_LEVEL_INFO, OTLKCON_LOG_TARGET_ALL, 
            L"%S sqlite3_prepare error",
            sqlite3_errmsg(sqlObj->databaseHandle));
    }


	for( ULONG i=0; i<cValues; i++ )
    {
        currPV=lpPropArray[i];
        //char *propName = prop2str->get( currPV.ulPropTag );
        //if( propName )
        //{
        //    OTLKCON_LOG_PRINTF(OTLKCON_LOG_LEVEL_INFO,  OTLKCON_LOG_TARGET_ALL, 
        //        L"<%S> %S->O_IProp::SetProps() : Putting 0x%0.8X ( %S )\n", 
        //        objName, className, currPV.ulPropTag, propName);
        //}
        //else
        //    OTLKCON_LOG_PRINTF(OTLKCON_LOG_LEVEL_INFO,  OTLKCON_LOG_TARGET_ALL, 
        //            L"<%S> %S->O_IProp::SetProps() : Putting 0x%0.8X\n", 
        //            objName, className, currPV.ulPropTag);

        //If there is
        //      a corresponding problem in the problem array,
        //      then skip this property. Do this right here
        if( lppProblems )
        {
            for(ULONG j=0; j<(*lppProblems)->cProblem; j++)
            {
                if( (*lppProblems)->aProblem[j].ulPropTag == currPV.ulPropTag )
                {
                    skip=1;
                    break;
                }
            }
        }

        if( skip )
        {
            skip=0;
            continue;
        }

        if( PROP_TYPE(currPV.ulPropTag) == PT_UNSPECIFIED )
            OTLKCON_DEBUGBREAK;

		switch( PROP_TYPE(currPV.ulPropTag) )
		{
		case PT_I2:
			/* short i */
			rowSize = 5*sizeof(ULONG)+sizeof(short);
            sqlRes = sqlite3_bind_int(queryStmt,1,rowSize);
            sqlRes = sqlite3_bind_int(queryStmt,2,currPV.ulPropTag);
            sqlRes = sqlite3_bind_int(queryStmt,3,0);
            sqlRes = sqlite3_bind_int(queryStmt,4,isTransacted?objNum:0);
            sqlRes = sqlite3_bind_int(queryStmt,5,0);
            sqlRes = sqlite3_bind_int(queryStmt, 6, currPV.Value.i);
			break;

		case PT_LONG:
			/* long l or ul??*/
			rowSize = 5*sizeof(ULONG)+sizeof(long);
            sqlRes = sqlite3_bind_int(queryStmt,1,rowSize);
            sqlRes = sqlite3_bind_int(queryStmt,2,currPV.ulPropTag);
            sqlRes = sqlite3_bind_int(queryStmt,3,0);
            sqlRes = sqlite3_bind_int(queryStmt,4,isTransacted?objNum:0);
            sqlRes = sqlite3_bind_int(queryStmt,5,0);
            sqlRes = sqlite3_bind_int(queryStmt, 6, currPV.Value.l);
			break;

		case PT_R4:
			/* float flt */
			rowSize = 5*sizeof(ULONG)+sizeof(float);
            sqlRes = sqlite3_bind_int(queryStmt,1,rowSize);
            sqlRes = sqlite3_bind_int(queryStmt,2,currPV.ulPropTag);
            sqlRes = sqlite3_bind_int(queryStmt,3,0);
            sqlRes = sqlite3_bind_int(queryStmt,4,isTransacted?objNum:0);
            sqlRes = sqlite3_bind_int(queryStmt,5,0);
            sqlRes = sqlite3_bind_double(queryStmt, 6, currPV.Value.flt);
			break;

		case PT_R8:
			/* double dbl */
			rowSize = 5*sizeof(ULONG)+sizeof(double);
            sqlRes = sqlite3_bind_int(queryStmt,1,rowSize);
            sqlRes = sqlite3_bind_int(queryStmt,2,currPV.ulPropTag);
            sqlRes = sqlite3_bind_int(queryStmt,3,0);
            sqlRes = sqlite3_bind_int(queryStmt,4,isTransacted?objNum:0);
            sqlRes = sqlite3_bind_int(queryStmt,5,0);
            sqlRes = sqlite3_bind_double(queryStmt, 6, currPV.Value.dbl);
			break;

		case PT_BOOLEAN:
			/* unsigned short int b*/
			rowSize = 5*sizeof(ULONG)+sizeof(unsigned short);
            sqlRes = sqlite3_bind_int(queryStmt,1,rowSize);
            sqlRes = sqlite3_bind_int(queryStmt,2,currPV.ulPropTag);
            sqlRes = sqlite3_bind_int(queryStmt,3,0);
            sqlRes = sqlite3_bind_int(queryStmt,4,isTransacted?objNum:0);
            sqlRes = sqlite3_bind_int(queryStmt,5,0);
            sqlRes = sqlite3_bind_int(queryStmt,5,currPV.Value.b);
            break;

		case PT_CURRENCY:
			/* CURRENCY cur */
			/* ??? not sure about this ??? */
			rowSize = 5*sizeof(ULONG)+sizeof(CURRENCY);
            sqlRes = sqlite3_bind_int(queryStmt,1,rowSize);
            sqlRes = sqlite3_bind_int(queryStmt,2,currPV.ulPropTag);
            sqlRes = sqlite3_bind_int(queryStmt,3,0);
            sqlRes = sqlite3_bind_int(queryStmt,4,isTransacted?objNum:0);
            sqlRes = sqlite3_bind_int(queryStmt,5,0);
            sqlRes = sqlite3_bind_blob(queryStmt, 6, &(currPV.Value.cur),
                    sizeof(currPV.Value.cur), SQLITE_TRANSIENT);
			break;

		case PT_APPTIME:
			/* double at */
            rowSize = 5*sizeof(ULONG)+sizeof(double);
            sqlRes = sqlite3_bind_int(queryStmt,1,rowSize);
            sqlRes = sqlite3_bind_int(queryStmt,2,currPV.ulPropTag);
            sqlRes = sqlite3_bind_int(queryStmt,3,0);
            sqlRes = sqlite3_bind_int(queryStmt,4,isTransacted?objNum:0);
            sqlRes = sqlite3_bind_int(queryStmt,5,0);
            sqlRes = sqlite3_bind_double(queryStmt, 6, currPV.Value.at);
            break;

		case PT_SYSTIME:
			/* FILETIME ft */
			rowSize = 5*sizeof(ULONG)+sizeof(currPV.Value.ft);
            sqlRes = sqlite3_bind_int(queryStmt,1,rowSize);
            sqlRes = sqlite3_bind_int(queryStmt,2,currPV.ulPropTag);
            sqlRes = sqlite3_bind_int(queryStmt,3,0);
            sqlRes = sqlite3_bind_int(queryStmt,4,isTransacted?objNum:0);
            sqlRes = sqlite3_bind_int(queryStmt,5,0);
            sqlRes = sqlite3_bind_blob(queryStmt, 6, &(currPV.Value.ft),
                    sizeof(currPV.Value.ft), SQLITE_TRANSIENT);
			break;

		case PT_STRING8:
			/* LPSTR lpszA */
            rowSize = 5*sizeof(ULONG)+sizeof(double);
            sqlRes = sqlite3_bind_int(queryStmt,1,rowSize);
            sqlRes = sqlite3_bind_int(queryStmt,2,currPV.ulPropTag);
            sqlRes = sqlite3_bind_int(queryStmt,3,0);
            sqlRes = sqlite3_bind_int(queryStmt,4,isTransacted?objNum:0);
            sqlRes = sqlite3_bind_int(queryStmt,5,0);
            sqlRes = sqlite3_bind_text(queryStmt, 6, currPV.Value.lpszA, -1, NULL);
			rowSize = 5*sizeof(ULONG)+(ULONG)strlen(currPV.Value.lpszA);
			break;

		case PT_BINARY:
			/* SBinary bin */
			rowSize = 5*sizeof(ULONG)+currPV.Value.bin.cb;
            sqlRes = sqlite3_bind_int(queryStmt,1,rowSize);
            sqlRes = sqlite3_bind_int(queryStmt,2,currPV.ulPropTag);
            sqlRes = sqlite3_bind_int(queryStmt,3,0);
            sqlRes = sqlite3_bind_int(queryStmt,4,isTransacted?objNum:0);
            sqlRes = sqlite3_bind_int(queryStmt,5,0);
            sqlRes = sqlite3_bind_blob(queryStmt, 6, currPV.Value.bin.lpb,
                    currPV.Value.bin.cb, SQLITE_TRANSIENT);
            break;

		case PT_UNICODE:
			/* LPWSTR lpszW */
			rowSize = 5*sizeof(ULONG)+(ULONG)wcslen(currPV.Value.lpszW);
            sqlRes = sqlite3_bind_int(queryStmt,1,rowSize);
            sqlRes = sqlite3_bind_int(queryStmt,2,currPV.ulPropTag);
            sqlRes = sqlite3_bind_int(queryStmt,3,0);
            sqlRes = sqlite3_bind_int(queryStmt,4,isTransacted?objNum:0);
            sqlRes = sqlite3_bind_int(queryStmt,5,0);
            sqlRes = sqlite3_bind_text16(queryStmt, 6, currPV.Value.lpszW, -1, NULL);
			rowSize = 5*sizeof(ULONG)+(ULONG)strlen(currPV.Value.lpszA);
			break;

		case PT_CLSID:
			// LPGUID lpguid 
			rowSize = 5*sizeof(ULONG)+sizeof(struct _GUID);
            sqlRes = sqlite3_bind_int(queryStmt,1,rowSize);
            sqlRes = sqlite3_bind_int(queryStmt,2,currPV.ulPropTag);
            sqlRes = sqlite3_bind_int(queryStmt,3,0);
            sqlRes = sqlite3_bind_int(queryStmt,4,isTransacted?objNum:0);
            sqlRes = sqlite3_bind_int(queryStmt,5,0);
            sqlRes = sqlite3_bind_blob(queryStmt, 6, currPV.Value.lpguid,
                    sizeof(struct _GUID), SQLITE_TRANSIENT);
			break;

		//case PT_I8:
		//	// LARGE_INTEGER li
		//	rowSize = 5*sizeof(ULONG)+sizeof(LARGE_INTEGER);
  //          sqlRes = sqlite3_bind_int(queryStmt,1,rowSize);
  //          sqlRes = sqlite3_bind_int(queryStmt,2,currPV.ulPropTag);
  //          sqlRes = sqlite3_bind_int(queryStmt,3,0);
  //          sqlRes = sqlite3_bind_int(queryStmt,4,isTransacted?objNum:0);
  //          sqlRes = sqlite3_bind_int(queryStmt,5,0);
  //          sqlRes = sqlite3_bind_int64(queryStmt,5,currPV.Value.li);
		//	break;

			// TODO: Fix multi-valued properties and transacted MV properties

		case PT_ERROR:
		case PT_NULL:
			OTLKCON_LOG_PRINTF(OTLKCON_LOG_LEVEL_INFO, OTLKCON_LOG_TARGET_ALL, 
				L"IProp::SetProps Setting a PT_ERROR or PT_NULL");
			//we do nothing
			continue;
			break;

		case PT_OBJECT:
			// This is PT_OBJECT only for internal use
			// NB: the value is not saved!!!
			// TODO:  Actually, what should happen is that the DATA column
			//        should hold the name of the table that represents this
			//        object.

            rowSize = 5*sizeof(ULONG)+1; // FIXME: This is wrong
            sqlRes = sqlite3_bind_int(queryStmt,1,rowSize);
            sqlRes = sqlite3_bind_int(queryStmt,2,currPV.ulPropTag);
            sqlRes = sqlite3_bind_int(queryStmt,3,0);
            sqlRes = sqlite3_bind_int(queryStmt,4,isTransacted?objNum:0);
            sqlRes = sqlite3_bind_int(queryStmt,5,1);
            sqlRes = sqlite3_bind_null(queryStmt,6);
			break;

		default:
			OTLKCON_LOG_PRINTF(OTLKCON_LOG_LEVEL_INFO, OTLKCON_LOG_TARGET_ALL, 
				L"ERROR: SetProps() : %d : unknown property type.\n", currPV.ulPropTag);
			continue;
			break;
		} 
		
		sqlRes = sqlite3_step(queryStmt);
		if( sqlRes != SQLITE_DONE )
		{
			OTLKCON_DEBUGBREAK;
		}

		sqlRes = sqlite3_reset(queryStmt);
		if( sqlRes != SQLITE_OK )
		{
			OTLKCON_DEBUGBREAK;
		}

        if( isTransacted )
        {
            // If the SetProp() call was successful, remove the
            // object from the deletedProperties list

            // TODO: Make sure this doesn't happen on failures
            deletedProperties.erase( PROP_ID(currPV.ulPropTag) );
        }
    } // for
	
	sqlRes = sqlObj->sqlCmdCommit();
	
    OTLKCON_LEAVECRITICALSECTION(&objectCS, "OBJ> IProp::SetProps");
    OTLKCON_LEAVECRITICALSECTION(&otlkcon_sqlite_cs, "SQL> O_IProp::SetProps");

    return result;

setprops_fail:
    sqlRes = sqlObj->sqlCmdRollback();
    OTLKCON_LEAVECRITICALSECTION(&objectCS, "OBJ> IProp::SetProps");
    OTLKCON_LEAVECRITICALSECTION(&otlkcon_sqlite_cs, "SQL> O_IProp::SetProps");

    return E_FAIL;
}

STDMETHODIMP O_IProp::DeleteProps(LPSPropTagArray lpPropTagArray,
                                        LPSPropProblemArray *lppProblems  )
{
    HRESULT result = S_OK;
    int sqlRes;
    LPSPropProblem tmpProp = NULL;
    pair< set<ULONG>::iterator, bool > findRes;

    //Validate_IMAPIProp_DeleteProps(this,lpPropTagArray,lppProblems);

    OTLKCON_LOG_PRINTF(OTLKCON_LOG_LEVEL_INFO,  OTLKCON_LOG_TARGET_ALL, 
        L"inside O_IProp::DeleteProps()\n" );

	OTLKCON_ENTERCRITICALSECTION(&otlkcon_sqlite_cs, "SQL> O_IProp::DeleteProps");
    OTLKCON_ENTERCRITICALSECTION(&objectCS, "OBJ> IProp::DeleteProps");

	otlkcon_sqlite_helper * sqlObj = GetSqlObj();
	if(NULL == sqlObj)
	{
		result = E_FAIL;
        goto deleteprops_fail;	
	} // if

	if( lppProblems )
    {
        // TODO: Should be free'ed on E_FAIL
        // FIXME: This array should be sized to the exact number of errors
		*lppProblems = NULL;
        *lppProblems 
            = (LPSPropProblemArray)memObj->malloc( CbNewSPropProblemArray(lpPropTagArray->cValues) );
    }
    
//	result = GetSqlObj();
    result = sqlObj->sqlCmdBegin();
	
	if(SUCCEEDED(result))
	{
		//char *propName;
		for( ULONG i=0; i<lpPropTagArray->cValues; i++)
		{
			//propName = msgstore->prop2str->get(lpPropTagArray->aulPropTag[i]);
			//if(propName)
			//{
			//	OTLKCON_LOG_PRINTF(OTLKCON_LOG_LEVEL_INFO,  OTLKCON_LOG_TARGET_ALL, 
			//		L"<%S> %S->O_IProp::DeleteProps() : Deleting 0x%0.8X ( %S )\n", 
			//		objName, className, lpPropTagArray->aulPropTag[i], propName);
			//	memObj->lpFreeBuffer(propName);
			//}
			//else
			//	OTLKCON_LOG_PRINTF(OTLKCON_LOG_LEVEL_INFO,  OTLKCON_LOG_TARGET_ALL, 
			//	L"<%S> %S->O_IProp::DeleteProps() : Deleting 0x%0.8X\n", 
			//	objName, className, lpPropTagArray->aulPropTag[i]);

			// Is this property computed?
			if( computedProperties.find( PROP_ID(lpPropTagArray->aulPropTag[i]) )
				!= computedProperties.end() )
			{
				// TODO: Set MAPI_E_NO_ACCESS
				if( lppProblems )
				{
					//FIXME: I know this is suspect
					(*lppProblems)->aProblem[i].ulIndex   = i;
					(*lppProblems)->aProblem[i].scode     = MAPI_E_COMPUTED;
					(*lppProblems)->aProblem[i].ulPropTag = lpPropTagArray->aulPropTag[i];
				}

				continue;
			}

			if( isTransacted )
			{
				// Add the properties to the 'deletedProperties' set
				if( deletedProperties.find( PROP_ID(lpPropTagArray->aulPropTag[i]) )
					!= deletedProperties.end() )
				{
					// Property has already been deleted
					if(NULL != lppProblems)
					{
						(*lppProblems)->aProblem[i].ulIndex   = i;
						(*lppProblems)->aProblem[i].scode     = MAPI_E_NOT_FOUND;
						(*lppProblems)->aProblem[i].ulPropTag = lpPropTagArray->aulPropTag[i];
					} // if
				}
				else
				{
					deletedProperties.insert( PROP_ID(lpPropTagArray->aulPropTag[i]) );

					// Remove shadow properties if any
					sqlRes = sqlObj->sqlCommand( "DELETE FROM %s WHERE (PropTag >> 16) = %d", 
						tableName, PROP_ID(lpPropTagArray->aulPropTag[i]) );
					if( sqlRes )
					{
						OTLKCON_DEBUGBREAK;
					}
				}
			}
			else
			{
				// We may have only been given the PropID
				// Remove those properties
				sqlRes = sqlObj->sqlCommand( "DELETE FROM %s WHERE (PropTag >> 16) = %d", 
					tableName, PROP_ID(lpPropTagArray->aulPropTag[i]) );
				if( sqlRes )
				{
					OTLKCON_DEBUGBREAK;
				}

				sqlRes = sqlite3_changes( sqlObj->databaseHandle );
				if( sqlRes == 0 )
				{
					// Property did not exist
					if(NULL != lppProblems)
					{
						(*lppProblems)->aProblem[i].ulIndex   = i;
						(*lppProblems)->aProblem[i].scode     = MAPI_E_NOT_FOUND;
						(*lppProblems)->aProblem[i].ulPropTag = lpPropTagArray->aulPropTag[i];
					} // if
				}
			}
		}

		result = sqlObj->sqlCmdCommit();

	} // if

deleteprops_fail:
    OTLKCON_LEAVECRITICALSECTION(&objectCS, "OBJ> IProp::DeleteProps");
    OTLKCON_LEAVECRITICALSECTION(&otlkcon_sqlite_cs, "SQL> O_IProp::DeleteProps");

    return result;
}

STDMETHODIMP O_IProp::CopyTo(ULONG ciidExclude,
  LPCIID rgiidExclude,
  LPSPropTagArray lpExcludeProps,
  ULONG ulUIParam,
  LPMAPIPROGRESS lpProgress,
  LPCIID lpInterface,
  LPVOID lpDestObj,
  ULONG ulFlags,
  LPSPropProblemArray *lppProblems  
)
{
    HRESULT result = S_OK;

    //Validate_IMAPIProp_CopyTo(this,ciidExclude,rgiidExclude,
      //  lpExcludeProps,ulUIParam,lpProgress,lpInterface,lpDestObj,ulFlags,
        //lppProblems);

    // TODO: Write a native version of this function

    OTLKCON_LOG_PRINTF(OTLKCON_LOG_LEVEL_INFO,  OTLKCON_LOG_TARGET_ALL, 
        L"inside O_IProp::CopyTo()\n" );

    if( ulFlags & MAPI_DECLINE_OK )
        return MAPI_E_DECLINE_COPY;

    result = msgstore->lpMAPISup->DoCopyTo((LPCIID)&(entryID->typeGuid), this,
        ciidExclude, rgiidExclude, lpExcludeProps, ulUIParam, lpProgress,
        lpInterface, lpDestObj, ulFlags, lppProblems );

    return result;
}

STDMETHODIMP O_IProp::CopyProps(
    LPSPropTagArray lpIncludeProps,
    ULONG ulUIParam,
    LPMAPIPROGRESS lpProgress,
    LPCIID lpInterface,
    LPVOID lpDestObj,
    ULONG ulFlags,
    LPSPropProblemArray *lppProblems  
)
{
    HRESULT result = MAPI_E_DECLINE_COPY;

//    Validate_IMAPIProp_CopyProps(this,lpIncludeProps,ulUIParam,
   //     lpProgress,lpInterface,lpDestObj,ulFlags,lppProblems);

    OTLKCON_LOG_PRINTF(OTLKCON_LOG_LEVEL_INFO,  OTLKCON_LOG_TARGET_ALL, 
        L"FIXME: inside O_IProp::CopyProps()\n" );
	
	result = msgstore->lpMAPISup->DoCopyProps(
		&IID_IMAPIProp, 
		(LPVOID)this,
		lpIncludeProps,
		ulUIParam,
		lpProgress,
		lpInterface,
		lpDestObj,
		ulFlags,
		lppProblems);

    return result;
}

STDMETHODIMP O_IProp::GetNamesFromIDs(
                        LPSPropTagArray *lppPropTags,
                        LPGUID lpPropSetGuid,
                        ULONG ulFlags,
                        ULONG *lpcPropNames,
                        LPMAPINAMEID **lpppPropNames  
)
{
    HRESULT result = S_OK;
    sqlite3_stmt * queryStmt;
    const char **queryRemainder = NULL;
    MAPIUID tmpGuid = {0};
    LPMAPINAMEID currNameID = NULL, propNames, *lpPropNames;
    int propCount,j,errs=0,sqlRes,count=0;
    char *queryStr, *name, *currGuid;
    ULONG tmpTag;

    //Validate_IMAPIProp_GetNamesFromIDs(this,lppPropTags,lpPropSetGuid,
     //                   ulFlags,lpcPropNames,lpppPropNames);
    lastCalledFunctionID = OTLKCON_IPROP_GETNAMESFROMIDS;
    strcpy_s(lastCalledFunctionName, OTLKCON_GETLASTERROR_FUNCTION_NAME_SIZE+1,
        OTLKCON_IPROP_GETNAMESFROMIDS_STR);
    result = SetLastError(S_OK, "");

    OTLKCON_LOG_PRINTF(OTLKCON_LOG_LEVEL_INFO,  OTLKCON_LOG_TARGET_ALL, 
        L"inside O_IProp::GetNamesFromIDs()\n" );

    OTLKCON_ENTERCRITICALSECTION(&otlkcon_sqlite_cs, "SQL> O_IProp::GetNamesFromIDs");
    OTLKCON_ENTERCRITICALSECTION(&objectCS, "OBJ> IProp::GetNamesFromIDs");
  
	otlkcon_sqlite_helper * sqlObj = GetSqlObj();
	if(NULL == sqlObj)
	{
		result = E_FAIL;
        goto getnamesfromids_end;
	} // if
	
	sqlObj->sqlCmdBegin();

    if( lppPropTags == NULL || *lppPropTags == NULL )
    {
        if( lpPropSetGuid == NULL )
        {
            //Count properties
            sqlRes = sqlObj->sqlCmdSingleInt( &propCount, 0,
                "SELECT COUNT(*) FROM namedproperty_mapping_table");
            *lpcPropNames = propCount;
            lpPropNames 
                = (LPMAPINAMEID *)memObj->malloc( propCount * sizeof( LPMAPINAMEID ) );
            if( lppPropTags )
            {
                *lppPropTags = (LPSPropTagArray)memObj->malloc( CbNewSPropTagArray(propCount) );
                (*lppPropTags)->cValues = propCount;
            }

            // Return all property names
            sqlRes = sqlObj->buildQuery( &queryStr,
                        "SELECT * FROM 'namedproperty_mapping_table'" );
            sqlite3_prepare( sqlObj->databaseHandle, queryStr, -1, 
                                &queryStmt, queryRemainder);
            j=0;
            while( (sqlRes = sqlite3_step(queryStmt)) == SQLITE_ROW )
            {
                if( lppPropTags )
                {
                    tmpTag = 0x8000 + sqlite3_column_int(queryStmt, 0);
                    if( tmpTag < 0x7FFF )
                    {
                        OTLKCON_DEBUGBREAK;
                    }
                    tmpTag <<= 16;
                    (*lppPropTags)->aulPropTag[j] = PROP_TAG(PT_UNSPECIFIED, PROP_ID(tmpTag));
                }
                propNames = (LPMAPINAMEID)memObj->malloc( sizeof(MAPINAMEID) );
                propNames->ulKind = sqlite3_column_int(queryStmt, 1);
                if( propNames->ulKind == MNID_ID && !(ulFlags & MAPI_NO_IDS ))
                {
                    propNames->Kind.lID = sqlite3_column_int(queryStmt, 2);
                }
                else if( propNames->ulKind == MNID_STRING && !(ulFlags & MAPI_NO_STRINGS ))
                {
                    name = (char *)sqlite3_column_text(queryStmt, 3);
                    propNames->Kind.lpwstrName 
                        = (LPWSTR)memObj->malloc( sizeof( wchar_t ) * (ULONG)strlen(name) );
                    swprintf_s(propNames->Kind.lpwstrName, strlen(name), L"%S", name);
                }
                else
                {
                    OTLKCON_DEBUGBREAK;
                }

                String2MAPIUID(&tmpGuid, (char *)sqlite3_column_text(queryStmt, 4));
                propNames->lpguid = (LPGUID)memObj->malloc( sizeof(GUID) );
                memcpy(propNames->lpguid, &tmpGuid, sizeof(GUID));
                lpPropNames[j++] = propNames;
                count++;
            }

            sqlRes = sqlite3_finalize(queryStmt);
            if ( sqlRes != SQLITE_OK )
            {
                OTLKCON_LOG_PRINTF(OTLKCON_LOG_LEVEL_INFO, OTLKCON_LOG_TARGET_ALL, 
                    L"IProp::GetNamesFromIDs sqlite3_finalize error: %S", 
                    sqlite3_errmsg(sqlObj->databaseHandle));
                result = E_FAIL;
                goto getnamesfromids_end;
            }
            *lpppPropNames = lpPropNames;
        }
        else
        {
            // Return all matching names in property set
            //Count properties
            currGuid = (char *)memObj->malloc(50);
            memcpy(&tmpGuid, lpPropSetGuid, sizeof(MAPIUID));
            result = MAPIUID2String(tmpGuid, &currGuid);

            sqlRes = sqlObj->sqlCmdSingleInt( &propCount, 0,
                "SELECT COUNT(*) FROM namedproperty_mapping_table WHERE PropertySet = '%s'", currGuid);
            *lpcPropNames = propCount;
            lpPropNames 
                = (LPMAPINAMEID *)memObj->malloc( propCount * sizeof( LPMAPINAMEID ) );
            if( lppPropTags )
            {
                *lppPropTags = (LPSPropTagArray)memObj->malloc( CbNewSPropTagArray(propCount) );
                (*lppPropTags)->cValues = propCount;
            }

            sqlRes = sqlObj->buildQuery( &queryStr,
                        "SELECT * FROM 'namedproperty_mapping_table' WHERE PropertySet = '%s'", currGuid);
            sqlite3_prepare( sqlObj->databaseHandle, queryStr, -1, 
                                &queryStmt, queryRemainder);
            j=0;
            while( (sqlRes = sqlite3_step(queryStmt)) == SQLITE_ROW )
            {
                if( lppPropTags )
                {
                    tmpTag = 0x8000 + sqlite3_column_int(queryStmt, 0);
                    if( tmpTag < 0x7FFF )
                    {
                        OTLKCON_DEBUGBREAK;
                    }
                    tmpTag <<= 16;
                    (*lppPropTags)->aulPropTag[j] = PROP_TAG(PT_UNSPECIFIED, PROP_ID(tmpTag));
                }

                propNames = (LPMAPINAMEID)memObj->malloc( sizeof(MAPINAMEID) );
                propNames->ulKind = sqlite3_column_int(queryStmt, 1);
                if( propNames->ulKind == MNID_ID && !(ulFlags & MAPI_NO_IDS ))
                {
                    propNames->Kind.lID = sqlite3_column_int(queryStmt, 2);
                }
                else if( propNames->ulKind == MNID_STRING && !(ulFlags & MAPI_NO_STRINGS ))
                {
                    name = (char *)sqlite3_column_text(queryStmt, 3);
                    propNames->Kind.lpwstrName 
                        = (LPWSTR)memObj->malloc( sizeof( wchar_t ) * (ULONG)strlen(name) );
                    swprintf_s(propNames->Kind.lpwstrName, strlen(name), L"%S", name);
                }
                else
                {
                    OTLKCON_DEBUGBREAK;
                }

                String2MAPIUID(&tmpGuid, (char *)sqlite3_column_text(queryStmt, 4));
                propNames->lpguid = (LPGUID)memObj->malloc( sizeof(GUID) );
                memcpy(propNames->lpguid, &tmpGuid, sizeof(GUID));
                lpPropNames[j++] = propNames;
                count++;
            }
            
            sqlRes = sqlite3_finalize(queryStmt);
            if ( sqlRes != SQLITE_OK )
            {
                OTLKCON_LOG_PRINTF(OTLKCON_LOG_LEVEL_INFO, OTLKCON_LOG_TARGET_ALL, 
                    L"IProp::GetNamesFromIDs sqlite3_finalize error: %S", 
                    sqlite3_errmsg(sqlObj->databaseHandle));
                result = E_FAIL;
                goto getnamesfromids_end;
            }

            *lpppPropNames = lpPropNames;
        }
    }
    else
    {
        // Return all matching names in mapping
        lpPropNames 
            = (LPMAPINAMEID *)memObj->malloc( (*lppPropTags)->cValues * sizeof( LPMAPINAMEID ) );
        *lpcPropNames = (*lppPropTags)->cValues;
        for(ULONG i=0; i<(*lppPropTags)->cValues; i++)
        {
            tmpTag = PROP_ID((*lppPropTags)->aulPropTag[i]) - 0x8000;
            sqlRes = sqlObj->buildQuery( &queryStr,
                "SELECT * FROM namedproperty_mapping_table \
                WHERE PropTag = %d", tmpTag );
            sqlRes = sqlite3_prepare( sqlObj->databaseHandle, queryStr, -1, 
                            &queryStmt, queryRemainder);
            if( (sqlRes = sqlite3_step(queryStmt)) == SQLITE_ROW )
            {
                // Found
                propNames = (LPMAPINAMEID)memObj->malloc( sizeof(MAPINAMEID) );
                propNames->ulKind = sqlite3_column_int(queryStmt, 1);
                if( propNames->ulKind == MNID_ID && !(ulFlags & MAPI_NO_IDS ))
                {
                    propNames->Kind.lID = sqlite3_column_int(queryStmt, 2);
                }
                else if( propNames->ulKind == MNID_STRING && !(ulFlags & MAPI_NO_STRINGS ))
                {
                    name = (char *)sqlite3_column_text(queryStmt, 3);
                    propNames->Kind.lpwstrName 
                        = (LPWSTR)memObj->malloc( sizeof( wchar_t ) * ((ULONG)strlen(name)+1) );
                    swprintf_s(propNames->Kind.lpwstrName, strlen(name)+1, L"%S", name);
                }
                else
                {
                    OTLKCON_DEBUGBREAK;
                }

                String2MAPIUID(&tmpGuid, (char *)sqlite3_column_text(queryStmt, 4));
                propNames->lpguid = (LPGUID)memObj->malloc( sizeof(GUID) );
                memcpy(propNames->lpguid, &tmpGuid, sizeof(GUID));
                lpPropNames[i] = propNames;
                count++;
            }
            else
            {
                errs = 1;
                lpPropNames[i] = NULL;
            }
                        
            sqlRes = sqlite3_finalize(queryStmt);
            if ( sqlRes != SQLITE_OK )
            {
                OTLKCON_LOG_PRINTF(OTLKCON_LOG_LEVEL_INFO, OTLKCON_LOG_TARGET_ALL, 
                    L"IProp::GetNamesFromIDs sqlite3_finalize error: %S", 
                    sqlite3_errmsg(sqlObj->databaseHandle));
                result = E_FAIL;
                goto getnamesfromids_end;
            }
        }
        *lpppPropNames = lpPropNames;
    }

    if(count==0)
    {
        *lpcPropNames = 0;
        *lpppPropNames = NULL;
        *lppPropTags = (LPSPropTagArray)memObj->malloc( CbNewSPropTagArray(1) );
        (*lppPropTags)->cValues = 0;
    }

getnamesfromids_end:

    sqlObj->sqlCmdCommit();

    OTLKCON_LEAVECRITICALSECTION(&objectCS, "OBJ> IProp::GetNamesFromIDs");
    OTLKCON_LEAVECRITICALSECTION(&otlkcon_sqlite_cs, "SQL> O_IProp::GetNamesFromIDs");

    if( errs && result == S_OK )
        return MAPI_W_ERRORS_RETURNED;

    return result;
}

STDMETHODIMP O_IProp::GetIDsFromNames(
  ULONG cPropNames,
  LPMAPINAMEID *lppPropNames,
  ULONG ulFlags,
  LPSPropTagArray *lppPropTags  
)
{
    HRESULT result = S_OK;
    int sqlRes;
    char *queryStr, *currGuid;
    vector<ULONG> tags;
    LPMAPINAMEID currNameID = NULL;
    int currProp, errs = 0;
    sqlite3_stmt *queryStmt, *idSelectStmt, *idInsertStmt,
        *strSelectStmt, *strInsertStmt;
    const char **queryRemainder = NULL;
    MAPIUID tmpGuid;
    __int64 id;

   // Validate_IMAPIProp_GetIDsFromNames(this,cPropNames,lppPropNames,
    //                   ulFlags,lppPropTags);
    lastCalledFunctionID = OTLKCON_IPROP_GETIDSFROMNAMES;
    strcpy_s(lastCalledFunctionName, OTLKCON_GETLASTERROR_FUNCTION_NAME_SIZE+1,
        OTLKCON_IPROP_GETIDSFROMNAMES_STR);
    result = SetLastError(S_OK, "");

    OTLKCON_LOG_PRINTF(OTLKCON_LOG_LEVEL_INFO,  OTLKCON_LOG_TARGET_ALL, 
        L"inside O_IProp::GetIDsFromNames()\n" );

    OTLKCON_ENTERCRITICALSECTION(&otlkcon_sqlite_cs, "SQL> O_IProp::GetIDsFromNames");
    OTLKCON_ENTERCRITICALSECTION(&objectCS, "OBJ> IProp::GetIDsFromNames");
 
	otlkcon_sqlite_helper * sqlObj = GetSqlObj();
	if(NULL == sqlObj)
	{
        OTLKCON_DEBUGBREAK;
        result = E_FAIL;
		goto getidsfromnames_end;
	} // if

	currGuid = (char *)memObj->malloc(50);

    result = sqlObj->sqlCmdBegin();

    if( lppPropNames == NULL )
    {
        // Requesting all named property tags
        if( ulFlags & MAPI_CREATE || cPropNames != 0 )
        {
            OTLKCON_DEBUGBREAK;
            result = MAPI_E_INVALID_PARAMETER;
            goto getidsfromnames_end;
        }

        sqlRes = sqlObj->buildQuery( &queryStr,
                        "SELECT PropTag FROM 'namedproperty_mapping_table'" );
        sqlite3_prepare( sqlObj->databaseHandle, queryStr, -1, 
                                &queryStmt, queryRemainder);
        while( (sqlRes = sqlite3_step(queryStmt)) == SQLITE_ROW )
        {
            tags.push_back( sqlite3_column_int(queryStmt,0) );
        }

        sqlRes = sqlite3_finalize(queryStmt);
        if ( sqlRes != SQLITE_OK )
        {
            OTLKCON_LOG_PRINTF(OTLKCON_LOG_LEVEL_INFO, OTLKCON_LOG_TARGET_ALL, 
                L"IProp::GetIDsFromNames sqlite3_finalize error: %S", 
                sqlite3_errmsg(sqlObj->databaseHandle));
            result = E_FAIL;
            goto getidsfromnames_end;
        }
        
        // build the PropTagArray
        *lppPropTags
            = (LPSPropTagArray)memObj->malloc( CbNewSPropTagArray((ULONG)tags.size()) );
        (*lppPropTags)->cValues = (ULONG)tags.size();

        for( ULONG i = 0; i<tags.size(); i++ )
        {
            (*lppPropTags)->aulPropTag[i] 
                = PROP_TAG(PT_UNSPECIFIED, tags[i]+0x8000);
        }
    }
    else
    {
        if( cPropNames < 1 )
        {
            OTLKCON_DEBUGBREAK;
            result = MAPI_E_INVALID_PARAMETER;
            goto getidsfromnames_end;
        }

        *lppPropTags
            = (LPSPropTagArray)memObj->malloc( CbNewSPropTagArray( cPropNames ) );
        (*lppPropTags)->cValues = cPropNames;

        sqlRes = sqlite3_prepare( sqlObj->databaseHandle, 
            "SELECT PropTag FROM namedproperty_mapping_table \
                        WHERE ( Property_set = ? ) AND\
                        ( id = ? ) AND ( ulKind = ? )",
                        -1, &idSelectStmt, 0);
        if(SQLITE_OK != sqlRes)
        {
	        OTLKCON_LOG_PRINTF(OTLKCON_LOG_LEVEL_INFO, OTLKCON_LOG_TARGET_ALL, 
		        L"%S sqlite3_prepare error",
		        sqlite3_errmsg(sqlObj->databaseHandle));
        }

        sqlRes = sqlite3_prepare( sqlObj->databaseHandle, 
            "INSERT OR REPLACE INTO namedproperty_mapping_table \
                                VALUES ( NULL, ?, ?, '', ? )",
                        -1, &idInsertStmt, 0);
        if(SQLITE_OK != sqlRes)
        {
	        OTLKCON_LOG_PRINTF(OTLKCON_LOG_LEVEL_INFO, OTLKCON_LOG_TARGET_ALL, 
		        L"%S sqlite3_prepare error",
		        sqlite3_errmsg(sqlObj->databaseHandle));
        }

        sqlRes = sqlite3_prepare( sqlObj->databaseHandle, 
            "SELECT PropTag FROM namedproperty_mapping_table \
                        WHERE ( Property_set = ? ) AND\
                        ( name = ? ) AND ( ulKind = ? )",
                        -1, &strSelectStmt, 0);
        if(SQLITE_OK != sqlRes)
        {
	        OTLKCON_LOG_PRINTF(OTLKCON_LOG_LEVEL_INFO, OTLKCON_LOG_TARGET_ALL, 
		        L"%S sqlite3_prepare error",
		        sqlite3_errmsg(sqlObj->databaseHandle));
        }

        sqlRes = sqlite3_prepare( sqlObj->databaseHandle, 
            "INSERT OR REPLACE INTO namedproperty_mapping_table \
                                VALUES ( NULL, ?, 0, ?, ? )",
                        -1, &strInsertStmt, 0);
        if(SQLITE_OK != sqlRes)
        {
	        OTLKCON_LOG_PRINTF(OTLKCON_LOG_LEVEL_INFO, OTLKCON_LOG_TARGET_ALL, 
		        L"%S sqlite3_prepare error",
		        sqlite3_errmsg(sqlObj->databaseHandle));
        }

        for( ULONG i=0; i<cPropNames; i++)
        {
            currNameID = lppPropNames[i];
            memset(currGuid,0,50);
            memcpy(&tmpGuid, currNameID->lpguid, sizeof(MAPIUID));
            result = MAPIUID2String(tmpGuid, &currGuid);

            if( currNameID->ulKind == MNID_ID )
            {
                sqlRes = sqlite3_bind_text(idSelectStmt, 1, currGuid, -1, NULL);
                sqlRes = sqlite3_bind_int(idSelectStmt,2,currNameID->Kind.lID);
                sqlRes = sqlite3_bind_int(idSelectStmt,3,currNameID->ulKind);

                sqlRes = sqlite3_step(idSelectStmt);
                switch( sqlRes )
                {
                    case SQLITE_DONE:
                    {
						sqlRes = sqlite3_reset(idSelectStmt);

                        if( ulFlags & MAPI_CREATE )
                        {
							

                            // FIXME:  This thing blows up after 1,999 named properties
                            //         have been assigned.  The proper thing to do would
                            //         be to scan for the lowest available id and return
                            //         and error if there weren't any.
                            sqlRes = sqlite3_bind_int(idInsertStmt,1,MNID_ID);
                            sqlRes = sqlite3_bind_int(idInsertStmt,2,currNameID->Kind.lID);
                            sqlRes = sqlite3_bind_text(idInsertStmt, 3, currGuid, -1, NULL);
                            
                            sqlRes = sqlite3_step(idInsertStmt);
                            switch( sqlRes )
                            {
                                case SQLITE_DONE:
                                    // Expected condition
                                    break;

                                case SQLITE_ROW:
                                    // We don't do anything with rows returned
                                    break;

                                default:
                                    // Some sought of error
                                    OTLKCON_DEBUGBREAK;
                                    break;
                            }
							sqlRes = sqlite3_reset(idInsertStmt);
                            
                            sqlRes = sqlObj->lastId( &id );
                            if( id > 0x7FFF )
                                OTLKCON_DEBUGBREAK;

                            currProp = 0x8000 + (__int32)id ;
                            currProp <<= 16;
                            (*lppPropTags)->aulPropTag[i] 
                                = PROP_TAG(PT_UNSPECIFIED, PROP_ID(currProp));
                        }
                        else
                        {
                            (*lppPropTags)->aulPropTag[i] 
                                = PROP_TAG(PT_ERROR, 0);
                            errs = 1;
                        }
                    }
                        break;

                    case SQLITE_ROW:
                        currProp = sqlite3_column_int(idSelectStmt, 0);
                        if( currProp > 0x7FFF )
                            OTLKCON_DEBUGBREAK;

						sqlRes = sqlite3_reset(idSelectStmt);

                        currProp += 0x8000;
                        //currProp <<= 16;
                        (*lppPropTags)->aulPropTag[i] = 
						    PROP_TAG(PT_UNSPECIFIED, currProp);
                        break;

                    default:
                        // Some sought of error
						sqlRes = sqlite3_reset(idSelectStmt);
                        OTLKCON_DEBUGBREAK;
                        break;
                }
            }
            else if( currNameID->ulKind == MNID_STRING )
            {
                sqlRes = sqlite3_bind_text(strSelectStmt, 1, currGuid, -1, NULL);
                sqlRes = sqlite3_bind_text16(strSelectStmt,2,currNameID->Kind.lpwstrName, -1, NULL);
                sqlRes = sqlite3_bind_int(strSelectStmt,3,currNameID->ulKind);

                sqlRes = sqlite3_step(strSelectStmt);
                switch( sqlRes )
                {
                    case SQLITE_DONE:
                    {
                        if( ulFlags & MAPI_CREATE )
                        {
							sqlRes = sqlite3_reset(strSelectStmt);

                            // FIXME:  This thing blows up after 1,999 named properties
                            //         have been assigned.  The proper thing to do would
                            //         be to scan for the lowest available id and return
                            //         and error if there weren't any.
                            sqlRes = sqlite3_bind_int(strInsertStmt,1,MNID_STRING);
                            sqlRes = sqlite3_bind_text16(strInsertStmt, 2, currNameID->Kind.lpwstrName, -1, NULL);
                            sqlRes = sqlite3_bind_text(strInsertStmt, 3, currGuid, -1, NULL);
                            
                            sqlRes = sqlite3_step(strInsertStmt);
                            switch( sqlRes )
                            {
                                case SQLITE_DONE:
                                    // Expected condition
                                    break;

                                default:
                                    // Some sought of error
                                    OTLKCON_DEBUGBREAK;
                                    break;
                            }
                            sqlRes = sqlite3_reset(strInsertStmt);
                            
                            sqlRes = sqlObj->lastId( &id );
                            if( id > 0x7FFF )
                                OTLKCON_DEBUGBREAK;

                            currProp = 0x8000 + (__int32)id ;
                            currProp <<= 16;
                            (*lppPropTags)->aulPropTag[i] 
                                = PROP_TAG(PT_UNSPECIFIED, PROP_ID(currProp));
                        }
                        else
                        {
                            (*lppPropTags)->aulPropTag[i] 
                                = PROP_TAG(PT_ERROR, 0);
                            errs = 1;
                        }
                    }
                        break;

                    case SQLITE_ROW:
                        currProp = sqlite3_column_int(strSelectStmt, 0);
                        if( currProp > 0x7FFF )
                            OTLKCON_DEBUGBREAK;

                        sqlRes = sqlite3_reset(strSelectStmt);

                        currProp += 0x8000;
                        //currProp <<= 16;
                        (*lppPropTags)->aulPropTag[i] = 
						    PROP_TAG(PT_UNSPECIFIED, currProp);
                        break;

                    default:
                        // Some sought of error
                        sqlRes = sqlite3_reset(strSelectStmt);
                        OTLKCON_DEBUGBREAK;
                        break;
                }
            }
            else
            {
                // Unknown ID type
                OTLKCON_DEBUGBREAK;
            }           
        }
    }

//    memObj->free(queryStr);

getidsfromnames_end:
    if( sqlObj )
    {
        sqlRes = sqlObj->sqlCmdCommit();
    }

    OTLKCON_LEAVECRITICALSECTION(&objectCS, "OBJ> IProp::GetIDsFromNames");
    OTLKCON_LEAVECRITICALSECTION(&otlkcon_sqlite_cs, "SQL> O_IProp::GetIDsFromNames");

    if( errs && result == S_OK )
        return MAPI_W_ERRORS_RETURNED;

    return result;
}

//Warning: OpenProperty() does not support storing non-contiguous
//         objects.  Eg. IMAPITable's with PT_OBJECT columns, etc.
STDMETHODIMP O_IProp::OpenProperty(ULONG ulPropTag,
  LPCIID lpiid,
  ULONG ulInterfaceOptions,
  ULONG ulFlags,
  LPUNKNOWN *lppUnk  
)
{
    HRESULT result  = S_OK;
    HGLOBAL mem     = 0;
    O_IStream *stream = NULL;
	SPropValue prop = {0};
    int sqlRes = S_OK;
    sqlite3_stmt * queryStmt = NULL;
    char * queryStr = NULL;
    const char **queryRemainder = NULL;

    OTLKCON_ENTERCRITICALSECTION(&otlkcon_sqlite_cs, "SQL> O_IProp::OpenProperty");
    OTLKCON_ENTERCRITICALSECTION(&objectCS, "OBJ> IProp::OpenProperty");
 
	otlkcon_sqlite_helper * sqlObj = GetSqlObj();
	if(NULL == sqlObj)
	{
		result = E_FAIL;	
        goto openproperty_fail;
	} // if
    
	//Validate seems to be currupting memory
    //Validate_IMAPIProp_OpenProperty(this,ulPropTag,lpiid,ulInterfaceOptions,ulFlags,lppUnk);
    lastCalledFunctionID = OTLKCON_IPROP_OPENPROPERTY;
    strcpy_s(lastCalledFunctionName, OTLKCON_GETLASTERROR_FUNCTION_NAME_SIZE+1,
        OTLKCON_IPROP_OPENPROPERTY_STR);
    result = SetLastError(S_OK, "");

    if( PROP_TYPE(ulPropTag) == PT_UNSPECIFIED || PROP_ID(ulPropTag) == 0 )
        OTLKCON_DEBUGBREAK;

    //char * propName = prop2str->get(ulPropTag);
    //if(propName)
    //{
    //    OTLKCON_LOG_PRINTF(OTLKCON_LOG_LEVEL_INFO,  OTLKCON_LOG_TARGET_ALL, 
    //        L"<%S> %S->O_IProp::OpenProperty() : Asking for 0x%0.8X ( %S )\n", 
    //            objName, className, ulPropTag, propName);
    //    memObj->lpFreeBuffer(propName);
    //}
    //else
    //    OTLKCON_LOG_PRINTF(OTLKCON_LOG_LEVEL_INFO,  OTLKCON_LOG_TARGET_ALL, 
    //        L"<%S> %S->O_IProp::OpenProperty() : Asking for 0x%0.8X ( %S )\n", 
    //        objName, className, ulPropTag);

    if( lppUnk == NULL )
    {
        SetLastError(E_FAIL, "Interface cannot be NULL");
        OTLKCON_DEBUGBREAK;
        result = E_FAIL;
        goto openproperty_fail;
    }

    if( PROP_TYPE(ulPropTag) != PT_OBJECT 
        && PROP_TYPE(ulPropTag) != PT_STRING8
		&& PROP_TYPE(ulPropTag) != PT_UNICODE
		&& PROP_TYPE(ulPropTag) != PT_BINARY)
    {
        //For now we only support PT_OBJECT
        SetLastError(MAPI_E_NO_SUPPORT, "Only PT_OBJECT and PT_STRING8 are supported");
        OTLKCON_DEBUGBREAK;
        result = MAPI_E_NO_SUPPORT;
        goto openproperty_fail;
    }

    sqlRes = sqlObj->sqlCmdBegin();

    if( !memcmp(lpiid, &IID_IStream, sizeof(IID)) )
    {
        // IStream

        //for IStream testing
        //    prop.ulPropTag = ulPropTag;
        //    prop.Value.x   = 0;
        //    result = _IntSetProps(1, &prop, NULL);

        stream = new O_IStream();
		result = stream->Create(databaseFilename, ulPropTag, 0, tableName, isTransacted?objNum:0,
                                    memObj->lpAllocateBuffer, memObj->lpAllocateMore, memObj->lpFreeBuffer);
        result = stream->Revert();
        if( FAILED(result) )
        {
            //We assume it is because the property did not exist
            if( !((ulFlags & MAPI_CREATE) && (ulFlags & MAPI_MODIFY)) )
            {
                // Property does not exist and we were not instruted
                // to create it.
                //OTLKCON_DEBUGBREAK;
                result = MAPI_E_NOT_FOUND;
                goto openproperty_fail;
            }
            
            prop.ulPropTag = ulPropTag;
            // For PT_OBJECT the value is ignored anyway
            prop.Value.lpszA   = memObj->_strdup("");
            result = _IntSetProps(1, &prop, NULL);

            //Try again
            result = stream->Revert();
            if( result != S_OK )
            {
                goto openproperty_fail;
            }
        }

        *lppUnk = stream;
    }
    else if( !memcmp(lpiid, &IID_IMAPITable, sizeof(IID)) )
    {
        // IMAPITable
        MAPIUID ig = {0};
        char *embedTableName = NULL;
        O_ITable *tbl = NULL;
        int found = 0;

        if( (ulFlags & MAPI_CREATE) && !(ulFlags & MAPI_MODIFY) )
        {
            // Create needs modify
            SetLastError(MAPI_E_NO_ACCESS, "Improper permissions");
            OTLKCON_DEBUGBREAK;
            result = MAPI_E_NO_ACCESS;
            goto openproperty_fail;
        }

        // Does the table exist ??
        sqlRes = sqlObj->buildQuery( &queryStr,
               "SELECT data FROM %s WHERE is_ref = 1 AND PropTag = %d;", 
                    tableName, ulPropTag );

        sqlite3_prepare( sqlObj->databaseHandle, queryStr, -1, 
                                    &queryStmt, queryRemainder);
        sqlRes = sqlite3_step(queryStmt);
        if( sqlRes == SQLITE_ROW )
        {
            // Get the table name
            embedTableName = memObj->pStrdup( (char *)sqlite3_column_text(queryStmt,0) );
            found = 1;
        }
        else if( sqlRes == SQLITE_DONE )
        {
            found = 0;
        }

        sqlRes = sqlite3_finalize(queryStmt);

        memObj->free(queryStr);

        if ( sqlRes != SQLITE_OK )
        {
            OTLKCON_LOG_PRINTF(OTLKCON_LOG_LEVEL_INFO, OTLKCON_LOG_TARGET_ALL, 
                L"IProp::OpenProperty sqlite3_finalize error: %S", 
                sqlite3_errmsg(sqlObj->databaseHandle));
            result = E_FAIL;
            goto openproperty_fail;
        }

        // Was the embedded table found ?
        if( found == 0 )
        {
            // Do we create?
            if( !(ulFlags & MAPI_CREATE) )
            {
                //Table doesn't exit and we can't create
                result = MAPI_E_NOT_FOUND;
                goto openproperty_fail;
            }

            // Create the table
            // TODO: Externally what good is a MAPI_CREATE
            //       using IMAPITable IID?  It's a read-only
            //       interface.
            tbl = new O_ITable();
            result = tbl->Create(databaseFilename,NULL,0,ig, msgstore );
            if( result != S_OK )
            {
                goto openproperty_fail;
            }

            // Set the reference in the O_IProp table
            sqlRes = sqlObj->sqlCommand(
                    "INSERT OR REPLACE INTO %s VALUES ( 0, %d, 0, 0, 1, '%s' )",
                        tableName, ulPropTag, tbl->tableName );
            if ( sqlRes != SQLITE_OK )
            {
                OTLKCON_LOG_PRINTF(OTLKCON_LOG_LEVEL_INFO, OTLKCON_LOG_TARGET_ALL, 
                    L"IProp::OpenProperty sqlite3_finalize error: %S", 
                    sqlite3_errmsg(sqlObj->databaseHandle));
                result = E_FAIL;
                goto openproperty_fail;
            }
        }
        else if ( found )
        {
            tbl = new O_ITable();

            if( ulFlags & MAPI_CREATE )
            {
                //We need to delete the existing table

                sqlObj->sqlCmdBegin();
                sqlRes = sqlObj->sqlCommand("DROP TABLE %s", embedTableName );
                result = tbl->Create(databaseFilename,NULL,0,ig,msgstore );
                if( result != S_OK )
                {
                    goto openproperty_fail;
                }

                // Set the reference in the O_IProp table
                sqlRes = sqlObj->sqlCommand(
                    "INSERT OR REPLACE INTO %s VALUES ( %d, 0, 0, 1, '%s' )",
                        tableName, ulPropTag, tbl->tableName );
                sqlObj->sqlCmdCommit();
            }
            else
            {
                // Open that table object      
                result = tbl->Load( databaseFilename, embedTableName,msgstore );
                if( result != S_OK )
                {
                    OTLKCON_LOG_PRINTF(OTLKCON_LOG_LEVEL_INFO, OTLKCON_LOG_TARGET_ALL, 
                        L"IProp::OpenProperty O_ITable::Load error" );
                    result = E_FAIL;
                    goto openproperty_fail;
                }
            }
        }

        *lppUnk = (LPUNKNOWN)tbl;
    }
    else if( !memcmp(lpiid, &IID_IMessage, sizeof(IID)) )
    {
        // IMessage      
        MAPIUID ig = {0};
        char *embedTableName = NULL;
        O_IMessage *msg = NULL;
        int found = 0;

        if( (ulFlags & MAPI_CREATE) && !(ulFlags & MAPI_MODIFY) )
        {
            // Create needs modify
            SetLastError(MAPI_E_NO_ACCESS, "Improper permissions");
            OTLKCON_DEBUGBREAK;
            result = MAPI_E_NO_ACCESS;
            goto openproperty_end;
        }

        // Does the table exist ??
        sqlRes = sqlObj->buildQuery( &queryStr,
               "SELECT data FROM %s WHERE is_ref = 1 AND PropTag = %d;", 
                    tableName, ulPropTag );

        sqlite3_prepare( sqlObj->databaseHandle, queryStr, -1, 
                                    &queryStmt, queryRemainder);
        sqlRes = sqlite3_step(queryStmt);
        if( sqlRes == SQLITE_ROW )
        {
            // Get the table name
            embedTableName = memObj->_strdup( (char *)sqlite3_column_text(queryStmt,0) );
            found = 1;
        }
        else if( sqlRes == SQLITE_DONE )
        {
            found = 0;
        }

        sqlRes = sqlite3_finalize(queryStmt);

        memObj->free(queryStr);

        if ( sqlRes != SQLITE_OK )
        {
            OTLKCON_LOG_PRINTF(OTLKCON_LOG_LEVEL_INFO, OTLKCON_LOG_TARGET_ALL, 
                L"IProp::OpenProperty sqlite3_finalize error: %S", 
                sqlite3_errmsg(sqlObj->databaseHandle));
            result = E_FAIL;
            goto openproperty_fail;
        }

        // Was the embedded message found ?
        if( found == 0 )
        {
            // Do we create?
            if( !(ulFlags & MAPI_CREATE) )
            {
                //Message doesn't exit and we can't create
                result = MAPI_E_NOT_FOUND;
                goto openproperty_fail;
            }

            // Create the message
            msg = new O_IMessage();
            result = msg->Create(msgstore, NULL, 0, ig );

            // Set the reference in the O_IProp table
            sqlRes = sqlObj->sqlCommand(
                    "INSERT OR REPLACE INTO %s VALUES ( 0, %d, 0, 0, 1, '%s' )",
                        tableName, ulPropTag, msg->tableName );
        }
        else if ( found )
        {
            msg = new O_IMessage();

            if( ulFlags & MAPI_CREATE )
            {
                //We need to delete the existing message

                sqlObj->sqlCmdBegin();
                sqlRes = sqlObj->sqlCommand("DROP TABLE %s", embedTableName );
                result = msg->Create(msgstore, NULL, 0, ig );

                // Set the reference in the O_IProp table
                sqlRes = sqlObj->sqlCommand(
                    "INSERT OR REPLACE INTO %s VALUES ( %d, 0, 0, 1, '%s' )",
                        tableName, ulPropTag, msg->tableName );
                sqlObj->sqlCmdCommit();
            }
            else
            {
                // Open that message object      
                result = msg->Load( msgstore, embedTableName );
                if( result != S_OK )
                {
                    goto openproperty_fail;
                }          
            }
        }

        *lppUnk = (LPUNKNOWN)msg;
    }
    else if( !memcmp(lpiid, &IID_IAttachment, sizeof(IID)) )
    {
        // IAttach

        // TODO: If needed 
        OTLKCON_DEBUGBREAK;

        result = MAPI_E_INTERFACE_NOT_SUPPORTED;
        goto openproperty_end;
    }
    else if( !memcmp(lpiid, &IID_IMAPIContainer, sizeof(IID)) )
    {
        // IMAPIContainer
        
        // We're not ready for PR_SEARCH
        result = MAPI_E_INTERFACE_NOT_SUPPORTED;
        goto openproperty_end;
    }
    else
    {
        //Is this a property we need to support??
        result = MAPI_E_INTERFACE_NOT_SUPPORTED;
        goto openproperty_end;
    }

openproperty_end:
    sqlRes = sqlObj->sqlCmdCommit();

    OTLKCON_LEAVECRITICALSECTION(&objectCS, "OBJ> IProp::OpenProperty");
    OTLKCON_LEAVECRITICALSECTION(&otlkcon_sqlite_cs, "SQL> O_IProp::OpenProperty");

    return result;

openproperty_fail:
    sqlRes = sqlObj->sqlCmdRollback();

    OTLKCON_LEAVECRITICALSECTION(&objectCS, "OBJ> IProp::OpenProperty");
    OTLKCON_LEAVECRITICALSECTION(&otlkcon_sqlite_cs, "SQL> O_IProp::OpenProperty");

    return result;
}

HRESULT O_IProp::MAPIUID2String( MAPIUID mu, char ** str )
{
    HRESULT result = S_OK;

    int resInt = sprintf_s(*str, 39,
    "{%0.2X%0.2X%0.2X%0.2X-%0.2X%0.2X-%0.2X%0.2X-%0.2X%0.2X-%0.2X%0.2X%0.2X%0.2X%0.2X%0.2X}",
    mu.ab[0], mu.ab[1],mu.ab[2],mu.ab[3],mu.ab[4],
    mu.ab[5],mu.ab[6],mu.ab[7],mu.ab[8],mu.ab[9],
    mu.ab[10],mu.ab[11],mu.ab[12],mu.ab[13],mu.ab[14],
    mu.ab[15]);

    if ( resInt == -1 )
    {
        return E_FAIL;
    }

    return result;
}

HRESULT O_IProp::String2MAPIUID( MAPIUID * mu, const char * str )
{
    HRESULT result = S_OK;
    unsigned char lastBytes[2] = {0};

    // TODO validate arguments & check the return value of sscanf
    int resInt = _snscanf_s(str, 38,
        "{%2hX%2hX%2hX%2hX-%2hX%2hX-%2hX%2hX-%2hX%2hX-%2hX%2hX%2hX%2hX%2hX%2hX}",
        &(mu->ab[0]), &(mu->ab[1]), &(mu->ab[2]), &(mu->ab[3]),
        &(mu->ab[4]), &(mu->ab[5]), &(mu->ab[6]), &(mu->ab[7]),
        &(mu->ab[8]), &(mu->ab[9]), &(mu->ab[10]), &(mu->ab[11]),
        &(mu->ab[12]), &(mu->ab[13]), &(mu->ab[14]), &lastBytes);
    mu->ab[15] = lastBytes[0];

    if ( resInt == EOF )
    {
        return E_FAIL;
    }

    return result;
}

HRESULT O_IProp::NewMAPIUID( MAPIUID *mu )
{
    HRESULT result = S_OK;

    if( mu == NULL )
        return E_FAIL;

    memset(mu, 0, sizeof(MAPIUID));
    for(int i=0; i<16; i++ )
    {
		::CoCreateGuid((GUID*)mu);
		//mu->ab[i] = rand()%256;
    }

    return result;
}

HRESULT O_IProp::NewEntryID( const GUID tg, LPENTRYID *eid )
{
    char *strBuf, *strBuf2;
    MAPIUID typeGuid;
    HRESULT result = S_OK;

	strBuf  = (char *)memObj->pMalloc( 40*sizeof(wchar_t) );
    strBuf2 = (char *)memObj->pMalloc( 40 );
    
    StringFromIID(  tg,  (LPWSTR*)&strBuf );
    _snprintf_s(strBuf2, 40, 39, "%S", strBuf);

    result = O_IProp::String2MAPIUID( &typeGuid, strBuf2);
    result = O_IProp::NewEntryID( typeGuid, eid );

    memObj->pFree( strBuf );
    memObj->pFree( strBuf2 );

    return result;
}

HRESULT O_IProp::NewEntryID( MAPIUID tg, LPENTRYID *eid )
{
    HRESULT result = S_OK;

    // FIXME: Is 'tg' being used at all? Why not?

    result = O_IProp::NewMAPIUID( &(entryID->instanceGuid) );

    return result;
}

HRESULT O_IProp::EntryID2String( O_ENTRYID *eid, char **str )
{
    HRESULT result = S_OK;
    char *istr =  (char *)calloc(1, 50);
    char *tstr =  (char *)calloc(1, 50);

    MAPIUID2String( eid->instanceGuid, &istr );
    MAPIUID2String( eid->typeGuid, &tstr );

    int intRes = sprintf_s( *str, 78, "%s-%s", tstr, istr);

    free(istr);
    free(tstr);

    return result;
}

HRESULT O_IProp::GetTableName( MAPIUID ig, MAPIUID tg, char **tbl )
{
    return GetTableName(ig,tg,tbl,memObj->lpAllocateBuffer);
}

HRESULT O_IProp::GetEidFromTableName( char *tbl, O_ENTRYID **eid, otlkcon_memory *mo , const MAPIUID *sg)
{
    HRESULT result = S_OK;
//    MAPIUID *ig = NULL;
//    MAPIUID *tg = NULL;
//    unsigned char lastBytes[2] = {0};
//    unsigned char lastBytes2[2] = {0};
    O_ENTRYID *tempEid = NULL;

	int nLength = (ULONG)strlen(tbl)+1;
	char * lpEntryString = NULL;

	if(1 != nLength)
	{
		lpEntryString = (char *)memObj->malloc(nLength);
		if(NULL == lpEntryString)
		{
			return MAPI_E_NOT_ENOUGH_MEMORY;
		} // if
		else
		{
			strcpy_s(lpEntryString, nLength, tbl);
		} // else
	} // if
	else
	{
		return MAPI_E_INVALID_ENTRYID;
	}

    tempEid = (O_ENTRYID*)mo->malloc( sizeof(O_ENTRYID) );
//    ig = (MAPIUID*)mo->malloc( sizeof(MAPIUID) );
//    tg = (MAPIUID*)mo->malloc( sizeof(MAPIUID) );

	char * lpReplace = strchr(lpEntryString, 'X');
	
	if(NULL != lpReplace)
	{
		*lpReplace = '{';
	} // if
	else
	{
		return MAPI_E_INVALID_ENTRYID;
	} // else
    lpReplace = strchr(lpEntryString, 'X');
	if(NULL != lpReplace)
	{
		*lpReplace = '{';
	} // if
	else
	{
		return MAPI_E_INVALID_ENTRYID;
	} // else
	
	lpReplace = strchr(lpEntryString, 'Y');
	if(NULL != lpReplace)
	{
		*lpReplace = '}';
	} // if
	else
	{
		return MAPI_E_INVALID_ENTRYID;
	} // else
	
	lpReplace = strchr(lpEntryString, 'Y');
	if(NULL != lpReplace)
	{
		*lpReplace = '}';
	} // if
	else
	{
		return MAPI_E_INVALID_ENTRYID;
	} // else
	
	//So far we get closer to read GUIDS
	char * typeguid = lpEntryString;
	char * instanceguid = NULL;

	lpReplace = strchr(lpEntryString, '_');
	while(NULL != lpReplace)
	{
		if(*(lpReplace-1)== '}' && *(lpReplace+1)== '{')
		{
			instanceguid = lpReplace+1;
			*lpReplace = '\0';
			++lpReplace;
		} // if
		else
		{
			*lpReplace = '-';
		} // else
		lpReplace = strchr(lpReplace, L'_');
	}
	if(NULL == instanceguid)
	{
		return MAPI_E_INVALID_ENTRYID;
	} // if
	
	MAPIUID tg = {0};
	MAPIUID ig = {0};
	
	if(FAILED(String2MAPIUID(&tg,typeguid)))
	{
		return MAPI_E_INVALID_ENTRYID;
	} // if
	
	if(FAILED(String2MAPIUID(&ig,instanceguid)))
	{
		return MAPI_E_INVALID_ENTRYID;
	} // if
	
/*
	// TODO validate arguments & check the return value of sscanf
    int resInt = _snscanf(tbl, 38,
        "X%2hX%2hX%2hX%2hX-%2hX%2hX-%2hX%2hX-%2hX%2hX-%2hX%2hX%2hX%2hX%2hX%2hXY_X%2hX%2hX%2hX%2hX-%2hX%2hX-%2hX%2hX-%2hX%2hX-%2hX%2hX%2hX%2hX%2hX%2hXY",
        &(ig->ab[0]), &(ig->ab[1]), &(ig->ab[2]), &(ig->ab[3]),
        &(ig->ab[4]), &(ig->ab[5]), &(ig->ab[6]), &(ig->ab[7]),
        &(ig->ab[8]), &(ig->ab[9]), &(ig->ab[10]), &(ig->ab[11]),
        &(ig->ab[12]), &(ig->ab[13]), &(ig->ab[14]), &lastBytes,
        &(tg->ab[0]), &(tg->ab[1]), &(tg->ab[2]), &(tg->ab[3]),
        &(tg->ab[4]), &(tg->ab[5]), &(tg->ab[6]), &(tg->ab[7]),
        &(tg->ab[8]), &(tg->ab[9]), &(tg->ab[10]), &(tg->ab[11]),
        &(tg->ab[12]), &(tg->ab[13]), &(tg->ab[14]), &lastBytes2);
    tg->ab[15] = lastBytes2[0];
    ig->ab[15] = lastBytes[0];
*/

    
	result = CreateEntryID(tempEid, sg, ig, tg);
//	memcpy(&(tempEid->instanceGuid), &ig, sizeof(MAPIUID));
//    memcpy(&(tempEid->typeGuid), &tg, sizeof(MAPIUID));

//	if(tg == otlkcon_imapifolder_guid)
//	{
//		tempEid->
//	} // if

    *eid = tempEid;

    if(NULL != lpEntryString)
    {
    	memObj->free(lpEntryString);
    } // if
	// TODO: Continue setting up the entry ID

    return result;
}

HRESULT O_IProp::GetTableName( MAPIUID ig, MAPIUID tg, char **tbl, 
                                                    LPALLOCATEBUFFER lb )
{
    HRESULT result = S_OK;

    char szTableName[MAX_PATH+1] = {0};
	char *lpStr = szTableName;
	O_IProp::MAPIUID2String(tg, &lpStr);
	strcat_s(szTableName, MAX_PATH, "-");
	char *tbl2 = (szTableName)+strlen(szTableName);
	O_IProp::MAPIUID2String(ig, &tbl2);
	for(ULONG i=0; i < strlen(szTableName); i++)
	{
		if ( szTableName[i] == '{' )
		{
			szTableName[i] = 'X';
		}
		else if ( szTableName[i] == '}' )
		{
			szTableName[i] = 'Y';
		}
		else if ( szTableName[i] == '-' )
		{
			szTableName[i] = '_';
		}
	}
	
	size_t cbNameLen = strlen(szTableName)+1;

//	cbNameLen += strlen("memDb.");
	result = lb((ULONG)cbNameLen, (LPVOID *)tbl);
	if(FAILED(result))
	{
		OTLKCON_DEBUGBREAK;
	} // if
	//There we can use membd for all tables just for testing

//    strcpy(*tbl, "memDb.");
	strcpy_s(*tbl, cbNameLen, szTableName);
/*
	memset(*tbl, 0, 80);
    O_IProp::MAPIUID2String(tg, tbl);
    strcat(*tbl, "-");
    char *tbl2 = (*tbl)+strlen(*tbl);
    O_IProp::MAPIUID2String(ig, &tbl2);

    for(ULONG i=0; i < strlen(*tbl); i++)
    {
        if ( (*tbl)[i] == '{' )
        {
            (*tbl)[i] = 'X';
        }
        else if ( (*tbl)[i] == '}' )
        {
            (*tbl)[i] = 'Y';
        }
        else if ( (*tbl)[i] == '-' )
        {
            (*tbl)[i] = '_';
        }
    }
*/

    return result;
}

HRESULT O_IProp::ObjectExists(O_ENTRYID *eid, HRESULT *value )
{
    HRESULT result = S_OK;
    int sqlRes;
    int rowCount;
    char *tbl;

    result = GetTableName(eid->instanceGuid, eid->typeGuid, &tbl);

    OTLKCON_ENTERCRITICALSECTION(&otlkcon_sqlite_cs, "SQL> O_IProp::ObjectExists");
    OTLKCON_ENTERCRITICALSECTION(&objectCS, "OBJ> IProp::ObjectExists");
 
	otlkcon_sqlite_helper * sqlObj = GetSqlObj();
	if(NULL == sqlObj)
	{
		result = E_FAIL;
        goto objectexists_fail;
	} // if

    //Does the table already exist?
    sqlRes = sqlObj->sqlCmdSingleInt( &rowCount, 0,
        "SELECT COUNT(*) FROM sqlite_master WHERE ( type='table' ) AND\
        (name='%s');", tbl);
    if( sqlRes != S_OK )
    {
        OTLKCON_DEBUGBREAK;
    }

    memObj->free(tbl);

    if ( rowCount == 0 )
        *value = FALSE;
    else *value = TRUE;

objectexists_fail:
    OTLKCON_LEAVECRITICALSECTION(&objectCS, "OBJ> IProp::ObjectExists");
    OTLKCON_LEAVECRITICALSECTION(&otlkcon_sqlite_cs, "SQL> O_IProp::ObjectExists");
    return result;
}

HRESULT O_IProp::ProcessEvent( otlkcon_event_struct *ev )
{
    HRESULT result = S_OK;

    return result;
}
